diff --git a/Makefile b/Makefile index 96ab268f..34084b44 100644 --- a/Makefile +++ b/Makefile @@ -274,8 +274,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_m1mangle_sym \ $(BIN)/test_m1usehint_run \ $(BIN)/test_m1union_run \ - $(BIN)/test_tupfieldsize_run \ - $(BIN)/test_tagtupfieldsize_run \ $(BIN)/test_nestfield_run \ $(BIN)/test_structlocal_frame \ $(BIN)/test_arrlit_tail_zero_run \ @@ -379,27 +377,16 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_alias_idx_family_run \ $(BIN)/test_idx_tagged_field_run \ $(BIN)/test_tagged_widen_arg_run \ - $(BIN)/test_tuple_tagged_union_run \ $(BIN)/test_alias_global_decl_run \ $(BIN)/test_alias_cgen_b5_run \ $(BIN)/test_alias_cgen_b6_run \ $(BIN)/test_alias_emit_b7_run \ $(BIN)/test_variant_chain_b95_run \ $(BIN)/test_peellint_gate \ - $(BIN)/test_tuple_nary_destructure_run \ - $(BIN)/test_rvalue_tuple_destructure_run \ - $(BIN)/test_tuple_lit_declblind_run \ - $(BIN)/test_overcap_tuple_field_store_run \ - $(BIN)/test_mixed_scalar_tuple_sret_run \ - $(BIN)/test_tuple_in_union_run \ - $(BIN)/test_nonlit_tuple_widen_run \ $(BIN)/test_const_slice_aggregate_run \ $(BIN)/test_append_structlit_evalorder_run \ - $(BIN)/test_tuple_index_read_run \ $(BIN)/test_f9_float_run \ $(BIN)/test_xmod_fnptr_const_run \ - $(BIN)/test_tuple_slot_layout_run \ - $(BIN)/test_tagged_tuple_widen_run \ $(BIN)/test_tagged_structlit_payload_run \ $(BIN)/test_forrange_fieldbase_run \ $(BIN)/test_errtype_compare \ @@ -580,7 +567,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_tuprecv_f64_run \ $(BIN)/test_assert_builtin_run \ $(BIN)/test_modqualdestr_run \ - $(BIN)/test_tupparam_run \ $(BIN)/test_structparam_run \ $(BIN)/test_structret_run \ $(BIN)/test_floats_run \ @@ -835,28 +821,6 @@ $(BIN)/test_m1union_run: test/wcc/989_m1union_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -# 989_tupfieldsize_run (F7-c4, #43): a for-range destructure over an array -# of tuples must stride by the tuple's true size (a slice/str/tuple field -# carries its full width). Builds+runs on BOTH driver twins (rule-10), the -# slice-field row pinned cs==ww (see the test header on the absolute value). -$(BIN)/test_tupfieldsize_run: test/wcc/989_tupfieldsize_run.c \ - $(BIN)/ww $(BIN)/ww_ww \ - $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -# 989_tagtupfieldsize_run (#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. Builds+runs on BOTH driver twins -# (rule-10), the tagged-field row pinned cs==ww at the absolute value. -$(BIN)/test_tagtupfieldsize_run: test/wcc/989_tagtupfieldsize_run.c \ - $(BIN)/ww $(BIN)/ww_ww \ - $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - # 989_nestfield_run (#44/#55): a struct with a nested sub-8 composite field # (inner{x:u8,y:u8}, slotsize 8 != size 2) plus a successor must address # every field at the checker's NATURAL offset on BOTH the write @@ -2266,12 +2230,6 @@ $(BIN)/test_tagged_widen_arg_run: test/wcc/944_tagged_widen_arg_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_tuple_tagged_union_run: test/wcc/944_tuple_tagged_union_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - $(BIN)/test_alias_global_decl_run: test/wcc/944_alias_global_decl_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -2305,67 +2263,6 @@ $(BIN)/test_variant_chain_b95_run: test/wcc/944_variant_chain_b95_run.c \ $(BIN)/test_peellint_gate: test/wcc/944_peellint_gate.c tools/peellint | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_tuple_nary_destructure_run: test/wcc/945_tuple_nary_destructure_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -# #241: materialise an RVALUE tuple (literal / match-yield / ?-call payload) -# into the register cursor before a destructure (run + cs==ww byte-id). -$(BIN)/test_rvalue_tuple_destructure_run: test/wcc/945_rvalue_tuple_destructure_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -# #64 + #68: tuple LITERAL fills the register cursor DECL-BLIND — the #57 -# decl wire (a declared-tagged element widens into its box) stopped at -# N_LET/N_RETURN; destructure-REASSIGN (#64) and CALL-ARG send (#68) still -# rode the decl-less route (box stored/sent word0-only, cursor skewed). -# Both stages, #263 gate-blind (byte-id cs==ww, both ran wrong). Run + -# cs==ww byte-id. -$(BIN)/test_tuple_lit_declblind_run: test/wcc/945_tuple_lit_declblind_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -# #234: over-cap tuple sret store into a local struct field / indexed local -# (run + cs==ww byte-id), deferred forms loud-stop (builderr both drivers). -$(BIN)/test_overcap_tuple_field_store_run: test/wcc/940_overcap_tuple_field_store_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -# #240: mixed-scalar tuple (e.g. (int,[]u8,str)) sret layout agrees in both -# stages — callee SEND foff via the return-type element size, caller RECEIVE -# falls an over-cap tuple through to the sret receive (size-gated). -$(BIN)/test_mixed_scalar_tuple_sret_run: test/wcc/940_mixed_scalar_tuple_sret_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -$(BIN)/test_tuple_in_union_run: test/wcc/940_tuple_in_union_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -$(BIN)/test_nonlit_tuple_widen_run: test/wcc/944_nonlit_tuple_widen_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - -$(BIN)/test_tuple_slot_layout_run: test/wcc/941_tuple_slot_layout_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - $(BIN)/test_const_slice_aggregate_run: test/wcc/946_const_slice_aggregate_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -2378,12 +2275,6 @@ $(BIN)/test_append_structlit_evalorder_run: test/wcc/946_append_structlit_evalor $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_tuple_index_read_run: test/wcc/947_tuple_index_read_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - $(BIN)/test_f9_float_run: test/wcc/949_f9_float_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -2396,12 +2287,6 @@ $(BIN)/test_xmod_fnptr_const_run: test/wcc/949_xmod_fnptr_const_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_tagged_tuple_widen_run: test/wcc/936_tagged_tuple_widen_run.c \ - $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ - $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - $(BIN)/test_tagged_structlit_payload_run: test/wcc/938_tagged_structlit_payload_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -3229,11 +3114,6 @@ $(BIN)/test_modqualdestr_run: test/wcc/956_modqualdestr_run.c $(BIN)/ww \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_tupparam_run: test/wcc/905_tupparam_run.c $(BIN)/ww \ - $(BIN)/w6c $(BIN)/w6c_ww $(BIN)/w6a $(BIN)/w6l \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - $(BIN)/test_structparam_run: test/wcc/946_structparam_run.c $(BIN)/ww \ $(BIN)/w6c $(BIN)/w6c_ww $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a | $(BIN) @@ -3310,7 +3190,7 @@ test-lang: all LANGBYTEID_DIR = $(OUT)/langbyteid LANGBYTEID_FILES = $(filter-out %_runonly_test.ww,$(wildcard test/lang/*_test.ww)) LANGBYTEID_VERB = test -c -LANGBYTEID_EXPECTED_MIN = 59 +LANGBYTEID_EXPECTED_MIN = 74 $(if $(LANGBYTEID_FILES),,$(error test-lang-byteid: empty corpus)) test-lang-byteid: all @set -e; \ diff --git a/test/lang/mixed_scalar_tuple_sret_test.ww b/test/lang/mixed_scalar_tuple_sret_test.ww new file mode 100644 index 00000000..a1a62d8a --- /dev/null +++ b/test/lang/mixed_scalar_tuple_sret_test.ww @@ -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)); +}; diff --git a/test/lang/nonlit_tuple_widen_test.ww b/test/lang/nonlit_tuple_widen_test.ww new file mode 100644 index 00000000..6c1d7f83 --- /dev/null +++ b/test/lang/nonlit_tuple_widen_test.ww @@ -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'))); + }; + }; +}; diff --git a/test/lang/overcap_tuple_field_store_test.ww b/test/lang/overcap_tuple_field_store_test.ww new file mode 100644 index 00000000..cdf2d4fe --- /dev/null +++ b/test/lang/overcap_tuple_field_store_test.ww @@ -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)); +}; diff --git a/test/lang/rvalue_tuple_destructure_test.ww b/test/lang/rvalue_tuple_destructure_test.ww new file mode 100644 index 00000000..e60bb6da --- /dev/null +++ b/test/lang/rvalue_tuple_destructure_test.ww @@ -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); }; + }; +}; diff --git a/test/lang/tagged_tuple_widen_test.ww b/test/lang/tagged_tuple_widen_test.ww new file mode 100644 index 00000000..f6564bde --- /dev/null +++ b/test/lang/tagged_tuple_widen_test.ww @@ -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); + }; +}; diff --git a/test/lang/tagtupfieldsize_test.ww b/test/lang/tagtupfieldsize_test.ww new file mode 100644 index 00000000..dae36d19 --- /dev/null +++ b/test/lang/tagtupfieldsize_test.ww @@ -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); +}; diff --git a/test/lang/tupfieldsize_test.ww b/test/lang/tupfieldsize_test.ww new file mode 100644 index 00000000..44176686 --- /dev/null +++ b/test/lang/tupfieldsize_test.ww @@ -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); +}; diff --git a/test/lang/tuple_in_union_test.ww b/test/lang/tuple_in_union_test.ww new file mode 100644 index 00000000..70e4bebf --- /dev/null +++ b/test/lang/tuple_in_union_test.ww @@ -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); }; + }; +}; diff --git a/test/lang/tuple_index_read_test.ww b/test/lang/tuple_index_read_test.ww new file mode 100644 index 00000000..8f22d799 --- /dev/null +++ b/test/lang/tuple_index_read_test.ww @@ -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); +}; diff --git a/test/lang/tuple_lit_declblind_test.ww b/test/lang/tuple_lit_declblind_test.ww new file mode 100644 index 00000000..fc73b889 --- /dev/null +++ b/test/lang/tuple_lit_declblind_test.ww @@ -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); +}; diff --git a/test/lang/tuple_nary_destructure_test.ww b/test/lang/tuple_nary_destructure_test.ww new file mode 100644 index 00000000..2d461bd1 --- /dev/null +++ b/test/lang/tuple_nary_destructure_test.ww @@ -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); +}; diff --git a/test/lang/tuple_slot_layout_test.ww b/test/lang/tuple_slot_layout_test.ww new file mode 100644 index 00000000..f3759ecd --- /dev/null +++ b/test/lang/tuple_slot_layout_test.ww @@ -0,0 +1,1453 @@ +// tuple_slot_layout_test — tuple arc layout SSoT (user-ratified): the tuple +// SLOT layout (str/slice = 24B header, everything else one 8B eightbyte; +// tagged = roundup8(size(elem))) is the single source of truth the checker +// size, the cgen cursor transports, and the t.N read walk all agree on. +// Migrated from test/wcc/941_tuple_slot_layout_run.c — the value rows become +// @test fns here (T1 `ww test` runs the runtime asserts; T2 test-lang-byteid +// keeps cs==ww); the reject rows become fold-3 //ww:error carriers under +// test/wcc/data/tslot_*/. Never-taken match arms / failure-checks that were +// `return K` become assert(false) / assert(!(COND)). +// +// Helper fns and types are suffixed with the row label to keep each migrated +// `package main` row's decls collision-free at this single file scope. + +package tuple_slot_layout_test; + +fn f_letcall_packed() (u32, u32) = { + return (3, 4); +}; + +@test fn letcall_packed() void = { + let t: (u32, u32) = f_letcall_packed(); + assert(!(t.0 != 3)); + assert(!(t.1 != 4)); +}; + +fn f_letcall_float_packed() (u32, f64) = { + let a: u32 = 9; + let x: f64 = 2.5; + return (a, x); +}; + +@test fn letcall_float_packed() void = { + let t: (u32, f64) = f_letcall_float_packed(); + assert(!(t.0 != 9)); + assert(!(t.1 != 2.5)); +}; + +fn f_letcall_signed_packed() (i32, i32) = { + let a: i32 = -5; + let b: i32 = -6; + return (a, b); +}; + +@test fn letcall_signed_packed() void = { + let t: (i32, i32) = f_letcall_signed_packed(); + assert(!(t.0 != -5)); + assert(!(t.1 != -6)); + assert(!(t.0 + t.1 != -11)); +}; + +fn f_letcall_mixed_packed() (u32, str) = { + let a: u32 = 7; + let s: str = "hello"; + return (a, s); +}; + +@test fn letcall_mixed_packed() void = { + let t: (u32, str) = f_letcall_mixed_packed(); + assert(!(t.0 != 7)); + assert(!(len(t.1) != 5)); +}; + +fn f_letcall_16_neutral() (i64, i64) = { + return (41, 17); +}; + +@test fn letcall_16_neutral() void = { + let t: (i64, i64) = f_letcall_16_neutral(); + assert(!(t.0 != 41)); + assert(!(t.1 != 17)); +}; + +fn f_letcall_32_neutral() (i64, str) = { + let a: i64 = 12; + let s: str = "wxyz"; + return (a, s); +}; + +@test fn letcall_32_neutral() void = { + let t: (i64, str) = f_letcall_32_neutral(); + assert(!(t.0 != 12)); + assert(!(len(t.1) != 4)); +}; + +fn f_mlet_packed_neutral() (u32, u32) = { + return (3, 4); +}; + +@test fn mlet_packed_neutral() void = { + let (a, b) = f_mlet_packed_neutral(); + assert(!(a != 3)); + assert(!(b != 4)); +}; + +@test fn c0_sizefold_slot() void = { + assert(!(size((u32, u32)) != 16)); + assert(!(size((size, size)) != 16)); + assert(!(size((u32, str)) != 32)); + assert(!(size((str, str)) != 48)); + assert(!(align((u32, u32)) != 4)); +}; + +type holder_c0_sizefold_recursive = struct { t: (u32, u32), x: i64 }; + +@test fn c0_sizefold_recursive() void = { + assert(!(size(holder_c0_sizefold_recursive) != 24)); + assert(!(size([4](u32, u32)) != 64)); +}; + +@test fn t22_sizefold_tagged() void = { + assert(!(size(((void | size), size)) != 24)); + assert(!(size((size, (void | size))) != 24)); + assert(!(size(((void | size), (void | size), size)) != 40)); + assert(!(align(((void | size), size)) != 8)); + assert(!(size((u64, void)) != 8)); +}; + +fn mk_t22_roundtrip() ((void | size), size) = { + let mn: (void | size) = 5: size; + return (mn, 4); +}; + +fn mk2_t22_roundtrip() (size, (void | size)) = { + let mx: (void | size) = 7: size; + return (9, mx); +}; + +fn mkv_t22_roundtrip() ((void | size), size) = { + let mn: (void | size) = void; + return (mn, 3); +}; + +@test fn t22_roundtrip() void = { + let t = mk_t22_roundtrip(); + assert(!(!(t.0 is size))); + assert(!(t.0 as size != 5)); + assert(!(t.1 != 4)); + let u = mk2_t22_roundtrip(); + assert(!(u.0 != 9)); + assert(!(!(u.1 is size))); + assert(!(u.1 as size != 7)); + let v = mkv_t22_roundtrip(); + assert(!(!(v.0 is void))); + assert(!(v.1 != 3)); + let (a, b) = mk_t22_roundtrip(); + assert(!(!(a is size))); + assert(!(a as size != 5)); + assert(!(b != 4)); + let mn: (void | size) = 6: size; + let w: ((void | size), size) = (mn, 2); + assert(!(!(w.0 is size))); + assert(!(w.0 as size != 6)); + assert(!(w.1 != 2)); +}; + +fn mk_t22_float_mix() (f64, (void | size), i64) = { + let mn: (void | size) = 6: size; + return (2.5, mn, 9); +}; + +@test fn t22_float_mix() void = { + let t = mk_t22_float_mix(); + assert(!(t.0 != 2.5)); + assert(!(!(t.1 is size))); + assert(!(t.1 as size != 6)); + assert(!(t.2 != 9)); + let (x, y, z) = mk_t22_float_mix(); + assert(!(x != 2.5)); + assert(!(!(y is size))); + assert(!(y as size != 6)); + assert(!(z != 9)); +}; + +fn mk_t22_two_tagged() ((void | u32), (void | i64)) = { + let a: (void | u32) = 9: u32; + let b: (void | i64) = -5: i64; + return (a, b); +}; + +fn mkv_t22_two_tagged() ((void | u32), (void | i64)) = { + let a: (void | u32) = void; + let b: (void | i64) = -41: i64; + return (a, b); +}; + +@test fn t22_two_tagged() void = { + let t = mk_t22_two_tagged(); + assert(!(!(t.0 is u32))); + assert(!(t.0 as u32 != 9)); + assert(!(!(t.1 is i64))); + assert(!(t.1 as i64 != -5)); + let v = mkv_t22_two_tagged(); + assert(!(!(v.0 is void))); + assert(!(v.1 as i64 != -41)); + let (a, b) = mk_t22_two_tagged(); + assert(!(a as u32 != 9)); + assert(!(b as i64 != -5)); +}; + +fn mk_t22_float_mix_rev() ((void | size), f64) = { + let mn: (void | size) = 6: size; + return (mn, 0.25); +}; + +@test fn t22_float_mix_rev() void = { + let t = mk_t22_float_mix_rev(); + assert(!(!(t.0 is size))); + assert(!(t.0 as size != 6)); + assert(!(t.1 != 0.25)); + let (y, z) = mk_t22_float_mix_rev(); + assert(!(y as size != 6)); + assert(!(z != 0.25)); +}; + +fn takes_t22_elem_arg(v: (void | size)) size = { + if (v is size) { return v as size; }; + return 0; +}; + +@test fn t22_elem_arg() void = { + let mn: (void | size) = 5: size; + let t: ((void | size), size) = (mn, 4); + assert(!(takes_t22_elem_arg(t.0) != 5)); + assert(!(takes_t22_elem_arg(t.0) + t.1 != 9)); +}; + +fn pr_b22_parse_repetition() ((void | size), (void | size), size) = { + let mn: (void | size) = void; + let mx: (void | size) = 5: size; + mn = 3: size; + return (mn, mx, 9); +}; + +@test fn b22_parse_repetition() void = { + let t = pr_b22_parse_repetition(); + assert(!(!(t.0 is size))); + assert(!(t.0 as size != 3)); + assert(!(!(t.1 is size))); + assert(!(t.1 as size != 5)); + assert(!(t.2 != 9)); + let (a, b, n) = pr_b22_parse_repetition(); + assert(!(a as size != 3)); + assert(!(b as size != 5)); + assert(!(n != 9)); + let a2: (void | size) = void; + let b2: (void | size) = void; + let n2: size = 0; + a2, b2, n2 = pr_b22_parse_repetition(); + assert(!(a2 as size != 3)); + assert(!(b2 as size != 5)); + assert(!(n2 != 9)); +}; + +fn ph_b22_head_mid_tail() ((void | size), size, size, size, size) = { + let a: (void | size) = 11: size; + return (a, 1, 2, 3, 4); +}; + +fn pm_b22_head_mid_tail() (size, size, (void | size), size, size) = { + let a: (void | size) = 12: size; + return (1, 2, a, 3, 4); +}; + +fn pt_b22_head_mid_tail() (size, size, size, size, (void | size)) = { + let a: (void | size) = 13: size; + return (1, 2, 3, 4, a); +}; + +fn pv_b22_head_mid_tail() ((void | size), (void | size), size) = { + let a: (void | size) = void; + let b: (void | size) = void; + return (a, b, 9); +}; + +@test fn b22_head_mid_tail() void = { + let h = ph_b22_head_mid_tail(); + assert(!(h.0 as size != 11)); + assert(!(h.4 != 4)); + let m = pm_b22_head_mid_tail(); + assert(!(m.0 != 1)); + assert(!(m.2 as size != 12)); + assert(!(m.4 != 4)); + let t = pt_b22_head_mid_tail(); + assert(!(t.3 != 4)); + assert(!(t.4 as size != 13)); + let v = pv_b22_head_mid_tail(); + assert(!(!(v.0 is void))); + assert(!(!(v.1 is void))); + assert(!(v.2 != 9)); + let (x, y, z) = v; + assert(!(!(x is void))); + assert(!(!(y is void))); + assert(!(z != 9)); +}; + +fn pr_b22_str_tagged_mix() (str, (void | size), size, size) = { + let mid: (void | size) = 4: size; + return ("hello", mid, 6, 7); +}; + +@test fn b22_str_tagged_mix() void = { + let t = pr_b22_str_tagged_mix(); + assert(!(t.0.len != 5)); + assert(!(t.1 as size != 4)); + assert(!(t.2 != 6)); + assert(!(t.3 != 7)); +}; + +fn pr_b22_sse_mix() ((void | size), f64, f64, f64) = { + let a: (void | size) = 6: size; + return (a, 1.5, 2.5, 3.5); +}; + +@test fn b22_sse_mix() void = { + let t = pr_b22_sse_mix(); + assert(!(t.0 as size != 6)); + assert(!(t.1 != 1.5)); + assert(!(t.2 != 2.5)); + assert(!(t.3 != 3.5)); + let (a, x, y, z) = pr_b22_sse_mix(); + assert(!(a as size != 6)); + assert(!(x != 1.5)); + assert(!(y != 2.5)); + assert(!(z != 3.5)); +}; + +fn pr_b22_underscore_skip() ((void | size), (void | size), size) = { + let mn: (void | size) = 3: size; + let mx: (void | size) = 5: size; + return (mn, mx, 9); +}; + +@test fn b22_underscore_skip() void = { + let a: (void | size) = void; + let n: size = 0; + a, _, n = pr_b22_underscore_skip(); + assert(!(a as size != 3)); + assert(!(n != 9)); + let (q, _, r) = pr_b22_underscore_skip(); + assert(!(q as size != 3)); + assert(!(r != 9)); +}; + +type four_c37_big_box_t0_read = struct { a: u64, b: u64, c: u64, d: u64, }; + +fn g_c37_big_box_t0_read() (void | four_c37_big_box_t0_read) = { + let v: four_c37_big_box_t0_read = four_c37_big_box_t0_read{a=1u64, b=2u64, c=3u64, d=4u64}; + return v; +}; + +fn pr_c37_big_box_t0_read() ((void | four_c37_big_box_t0_read), size) = { + let e: (void | four_c37_big_box_t0_read) = g_c37_big_box_t0_read(); + return (e, 7); +}; + +@test fn c37_big_box_t0_read() void = { + let t = pr_c37_big_box_t0_read(); + assert(!(t.1 != 7)); + assert(!(!(t.0 is four_c37_big_box_t0_read))); + match (t.0) { + case let f: four_c37_big_box_t0_read => { + assert(!(f.a != 1)); + assert(!(f.b != 2)); + assert(!(f.c != 3)); + assert(!(f.d != 4)); + }; + case void => { assert(false); }; + }; +}; + +type inst_lit_c37_idx_match_56 = rune; +type inst_repeat_c37_idx_match_56 = struct { + id: size, + origin: size, + min: (void | size), + max: (void | size), +}; +type inst_c37_idx_match_56 = (inst_lit_c37_idx_match_56 | inst_repeat_c37_idx_match_56); + +@test fn c37_idx_match_56() void = { + let insts: []inst_c37_idx_match_56 = []; + let r = inst_repeat_c37_idx_match_56{id=7: size, origin=3: size, min=(11: size), max=(22: size)}; + append(insts, r); + append(insts, ('A': inst_lit_c37_idx_match_56)); + let pc: size = 0; + match (insts[pc]) { + case let ir: inst_repeat_c37_idx_match_56 => { + assert(!(ir.id != 7)); + assert(!(ir.origin != 3)); + assert(!(!(ir.min is size))); + assert(!(ir.min as size != 11)); + assert(!(!(ir.max is size))); + assert(!(ir.max as size != 22)); + }; + case let l: inst_lit_c37_idx_match_56 => { assert(false); }; + }; + match (insts[1]) { + case let l: inst_lit_c37_idx_match_56 => { + assert(!(l != 'A')); + }; + case let ir: inst_repeat_c37_idx_match_56 => { assert(false); }; + }; +}; + +type lit2_c37_idx_match_str_nested = rune; +type rep2_c37_idx_match_str_nested = struct { + name: str, + min: (void | size), + max: (void | size), +}; +type in2_c37_idx_match_str_nested = (lit2_c37_idx_match_str_nested | rep2_c37_idx_match_str_nested); + +@test fn c37_idx_match_str_nested() void = { + let xs: []in2_c37_idx_match_str_nested = []; + let r = rep2_c37_idx_match_str_nested{name="deadbeef", min=(2: size), max=(5: size)}; + append(xs, r); + match (xs[0]) { + case let v: rep2_c37_idx_match_str_nested => { + assert(!(v.name.len != 8)); + assert(!(v.name[0] != 'd')); + assert(!(v.name[7] != 'f')); + assert(!(!(v.min is size))); + assert(!(v.min as size != 2)); + assert(!(v.max as size != 5)); + }; + case let l: lit2_c37_idx_match_str_nested => { assert(false); }; + }; +}; + +type inst_lit_c37_idx_let_is_assign = rune; +type inst_repeat_c37_idx_let_is_assign = struct { + id: size, + origin: size, + min: (void | size), + max: (void | size), +}; +type inst_c37_idx_let_is_assign = (inst_lit_c37_idx_let_is_assign | inst_repeat_c37_idx_let_is_assign); + +@test fn c37_idx_let_is_assign() void = { + let insts: []inst_c37_idx_let_is_assign = []; + let r = inst_repeat_c37_idx_let_is_assign{id=7: size, origin=3: size, min=(11: size), max=(22: size)}; + append(insts, r); + append(insts, ('A': inst_lit_c37_idx_let_is_assign)); + assert(!(!(insts[0] is inst_repeat_c37_idx_let_is_assign))); + assert(!(insts[0] is inst_lit_c37_idx_let_is_assign)); + let w = insts[0]; + match (w) { + case let ir: inst_repeat_c37_idx_let_is_assign => { + assert(!(ir.max as size != 22)); + }; + case let l: inst_lit_c37_idx_let_is_assign => { assert(false); }; + }; + w = insts[1]; + assert(!(!(w is inst_lit_c37_idx_let_is_assign))); + w = insts[0]; + match (w) { + case let ir: inst_repeat_c37_idx_let_is_assign => { + assert(!(ir.min as size != 11)); + }; + case let l: inst_lit_c37_idx_let_is_assign => { assert(false); }; + }; +}; + +type inst_lit_c37_idx_return = rune; +type inst_repeat_c37_idx_return = struct { + id: size, + origin: size, + min: (void | size), + max: (void | size), +}; +type inst_c37_idx_return = (inst_lit_c37_idx_return | inst_repeat_c37_idx_return); + +fn pick_c37_idx_return(insts: []inst_c37_idx_return, pc: size) inst_c37_idx_return = { + return insts[pc]; +}; + +@test fn c37_idx_return() void = { + let insts: []inst_c37_idx_return = []; + let r = inst_repeat_c37_idx_return{id=7: size, origin=3: size, min=(11: size), max=(22: size)}; + append(insts, r); + let w = pick_c37_idx_return(insts, 0); + match (w) { + case let ir: inst_repeat_c37_idx_return => { + assert(!(ir.id != 7)); + assert(!(ir.min as size != 11)); + }; + case let l: inst_lit_c37_idx_return => { assert(false); }; + }; +}; + +type inst_lit_c37_widen_and_memarg = rune; +type inst_repeat_c37_widen_and_memarg = struct { + id: size, + origin: size, + min: (void | size), + max: (void | size), +}; +type inst_c37_widen_and_memarg = (inst_lit_c37_widen_and_memarg | inst_repeat_c37_widen_and_memarg); +type wide_c37_widen_and_memarg = (inst_lit_c37_widen_and_memarg | inst_repeat_c37_widen_and_memarg | str); + +fn idof_c37_widen_and_memarg(i: inst_c37_widen_and_memarg) size = { + match (i) { + case let ir: inst_repeat_c37_widen_and_memarg => { return ir.id; }; + case let l: inst_lit_c37_widen_and_memarg => { return 999; }; + }; + return 998; +}; + +@test fn c37_widen_and_memarg() void = { + let insts: []inst_c37_widen_and_memarg = []; + let r = inst_repeat_c37_widen_and_memarg{id=7: size, origin=3: size, min=(11: size), max=(22: size)}; + append(insts, r); + let w: wide_c37_widen_and_memarg = insts[0]; + match (w) { + case let ir: inst_repeat_c37_widen_and_memarg => { + assert(!(ir.max as size != 22)); + }; + case let l: inst_lit_c37_widen_and_memarg => { assert(false); }; + case let s2: str => { assert(false); }; + }; + assert(!(idof_c37_widen_and_memarg(insts[0]) != 7)); +}; + +type inst_lit_c37_widen_remap = rune; +type inst_repeat_c37_widen_remap = struct { + id: size, + origin: size, + min: (void | size), + max: (void | size), +}; +type rev_c37_widen_remap = (inst_repeat_c37_widen_remap | inst_lit_c37_widen_remap); +type wide_c37_widen_remap = (inst_lit_c37_widen_remap | inst_repeat_c37_widen_remap | str); + +@test fn c37_widen_remap() void = { + let xs: []rev_c37_widen_remap = []; + let r = inst_repeat_c37_widen_remap{id=7: size, origin=3: size, min=(11: size), max=(22: size)}; + append(xs, r); + let w: wide_c37_widen_remap = xs[0]; + match (w) { + case let ir: inst_repeat_c37_widen_remap => { + assert(!(ir.id != 7)); + assert(!(!(ir.max is size))); + assert(!(ir.max as size != 22)); + }; + case let l: inst_lit_c37_widen_remap => { assert(false); }; + case let s2: str => { assert(false); }; + }; +}; + +type st_c37_x1_compose = struct { a: size, m: (void | size), }; + +fn pr_c37_x1_compose() ((void | st_c37_x1_compose), size, size) = { + let e: (void | st_c37_x1_compose) = st_c37_x1_compose{a=4: size, m=(2: size)}; + return (e, 5, 6); +}; + +@test fn c37_x1_compose() void = { + let t = pr_c37_x1_compose(); + assert(!(!(t.0 is st_c37_x1_compose))); + match (t.0) { + case let v: st_c37_x1_compose => { + assert(!(v.a != 4)); + assert(!(!(v.m is size))); + assert(!(v.m as size != 2)); + }; + case void => { assert(false); }; + }; + assert(!(t.1 != 5)); + assert(!(t.2 != 6)); +}; + +type box_c37_idx_at_cap = (void | str); + +@test fn c37_idx_at_cap() void = { + let bs: []box_c37_idx_at_cap = []; + let s: str = "abcdefgh"; + append(bs, s); + match (bs[0]) { + case let v: str => { + assert(!(v.len != 8)); + }; + case void => { assert(false); }; + }; +}; + +type inst_lit_c37_ptr_field_match = rune; +type inst_repeat_c37_ptr_field_match = struct { + id: size, + origin: size, + min: (void | size), + max: (void | size), +}; +type inst_c37_ptr_field_match = (inst_lit_c37_ptr_field_match | inst_repeat_c37_ptr_field_match); +type holder_c37_ptr_field_match = struct { pre: size, f: inst_c37_ptr_field_match, }; + +@test fn c37_ptr_field_match() void = { + let g = holder_c37_ptr_field_match{pre=9: size, f=inst_repeat_c37_ptr_field_match{id=7: size, origin=3: size, min=(11: size), max=(55: size)}}; + let p = &g; + assert(!(!(p.f is inst_repeat_c37_ptr_field_match))); + match (p.f) { + case let ir: inst_repeat_c37_ptr_field_match => { + assert(!(ir.id != 7)); + assert(!(ir.max as size != 55)); + }; + case let l: inst_lit_c37_ptr_field_match => { assert(false); }; + }; +}; + +type inst_lit_c37_deref_idx_match = rune; +type inst_repeat_c37_deref_idx_match = struct { + id: size, + origin: size, + min: (void | size), + max: (void | size), +}; +type inst_c37_deref_idx_match = (inst_lit_c37_deref_idx_match | inst_repeat_c37_deref_idx_match); + +@test fn c37_deref_idx_match() void = { + let insts: []inst_c37_deref_idx_match = []; + let r = inst_repeat_c37_deref_idx_match{id=7: size, origin=3: size, min=(11: size), max=(22: size)}; + append(insts, r); + let p = &insts; + match ((*p)[0]) { + case let ir: inst_repeat_c37_deref_idx_match => { + assert(!(ir.id != 7)); + assert(!(ir.max as size != 22)); + }; + case let l: inst_lit_c37_deref_idx_match => { assert(false); }; + }; +}; + +type inst_lit_fc_deref_56_slice_let = rune; +type inst_repeat_fc_deref_56_slice_let = struct { + id: size, + origin: size, + min: (void | size), + max: (void | size), +}; +type inst_fc_deref_56_slice_let = (inst_lit_fc_deref_56_slice_let | inst_repeat_fc_deref_56_slice_let); + +@test fn fc_deref_56_slice_let() void = { + let insts: []inst_fc_deref_56_slice_let = []; + let r = inst_repeat_fc_deref_56_slice_let{id=7: size, origin=3: size, min=(11: size), max=(22: size)}; + append(insts, r); + let p = &insts[0]; + let w = *p; + assert(!(!(w is inst_repeat_fc_deref_56_slice_let))); + match (w) { + case let ir: inst_repeat_fc_deref_56_slice_let => { + assert(!(ir.id != 7 || ir.origin != 3)); + assert(!((ir.min as size) != 11)); + assert(!((ir.max as size) != 22)); + }; + case let l: inst_lit_fc_deref_56_slice_let => { assert(false); }; + }; +}; + +type inst_lit_fc_deref_56_slice_match = rune; +type inst_repeat_fc_deref_56_slice_match = struct { + id: size, + origin: size, + min: (void | size), + max: (void | size), +}; +type inst_fc_deref_56_slice_match = (inst_lit_fc_deref_56_slice_match | inst_repeat_fc_deref_56_slice_match); + +@test fn fc_deref_56_slice_match() void = { + let insts: []inst_fc_deref_56_slice_match = []; + let r = inst_repeat_fc_deref_56_slice_match{id=7: size, origin=3: size, min=(11: size), max=(22: size)}; + append(insts, r); + let p = &insts[0]; + match (*p) { + case let ir: inst_repeat_fc_deref_56_slice_match => { + assert(!(ir.id != 7)); + assert(!(!(ir.min is size))); + assert(!((ir.max as size) != 22)); + }; + case let l: inst_lit_fc_deref_56_slice_match => { assert(false); }; + }; +}; + +type four_b22_big_box_mlet = struct { a: u64, b: u64, c: u64, d: u64, }; + +fn g_b22_big_box_mlet() (void | four_b22_big_box_mlet) = { + let v: four_b22_big_box_mlet = four_b22_big_box_mlet{a=1u64, b=2u64, c=3u64, d=4u64}; + return v; +}; + +fn pr_b22_big_box_mlet() ((void | four_b22_big_box_mlet), size) = { + let e: (void | four_b22_big_box_mlet) = g_b22_big_box_mlet(); + return (e, 7); +}; + +@test fn b22_big_box_mlet() void = { + let e2 = g_b22_big_box_mlet(); + assert(!(!(e2 is four_b22_big_box_mlet))); + let (e, n) = pr_b22_big_box_mlet(); + assert(!(n != 7)); + assert(!(!(e is four_b22_big_box_mlet))); +}; + +fn mk_b22_void_elem_k4() (void, size, size, size, size, size) = { + return (void, 1, 2, 3, 4, 5); +}; + +@test fn b22_void_elem_k4() void = { + let (v, a, b, c, d, e) = mk_b22_void_elem_k4(); + assert(!(a != 1)); + assert(!(b != 2)); + assert(!(c != 3)); + assert(!(d != 4)); + assert(!(e != 5)); +}; + +fn mk_b22_k5_box_at_cap() ((void | str), size, size) = { + let m: (void | str) = "boundary"; + return (m, 5, 6); +}; + +@test fn b22_k5_box_at_cap() void = { + let t = mk_b22_k5_box_at_cap(); + assert(!(!(t.0 is str))); + match (t.0) { + case let s: str => { + assert(!(s.len != 8)); + }; + case void => { assert(false); }; + }; + assert(!(t.1 != 5)); + assert(!(t.2 != 6)); +}; + +fn pr_b22_both_tagged_2elem() ((void | str), (void | size)) = { + let a: (void | str) = "boundary"; + let b: (void | size) = 7: size; + return (a, b); +}; + +@test fn b22_both_tagged_2elem() void = { + let t = pr_b22_both_tagged_2elem(); + assert(!(!(t.0 is str))); + match (t.0) { + case let s: str => { assert(!(s.len != 8)); }; + case void => { assert(false); }; + }; + assert(!(!(t.1 is size))); + assert(!(t.1 as size != 7)); + let (x, y) = pr_b22_both_tagged_2elem(); + assert(!(!(x is str))); + assert(!(y as size != 7)); +}; + +fn g_b22_chained_sret() ((void | size), (void | size), size) = { + let mn: (void | size) = 3: size; + let mx: (void | size) = 5: size; + return (mn, mx, 9); +}; + +fn h_b22_chained_sret() ((void | size), (void | size), size) = { + let (a, b, n) = g_b22_chained_sret(); + return (a, b, n + 1); +}; + +@test fn b22_chained_sret() void = { + let t = h_b22_chained_sret(); + assert(!(t.0 as size != 3)); + assert(!(t.1 as size != 5)); + assert(!(t.2 != 10)); +}; + +fn f_sret_narrow_mix_let() (str, u32, str) = { + let a: str = "abc"; + let b: u32 = 7; + let c: str = "wxyz"; + return (a, b, c); +}; + +@test fn sret_narrow_mix_let() void = { + let t = f_sret_narrow_mix_let(); + assert(!(len(t.0) != 3)); + assert(!(t.1 != 7)); + assert(!(len(t.2) != 4)); +}; + +fn f_sret_narrow_mix_mlet() (str, u32, str) = { + let a: str = "abc"; + let b: u32 = 7; + let c: str = "wxyz"; + return (a, b, c); +}; + +@test fn sret_narrow_mix_mlet() void = { + let (a, b, c) = f_sret_narrow_mix_mlet(); + assert(!(len(a) != 3)); + assert(!(b != 7)); + assert(!(len(c) != 4)); +}; + +fn f_sret_narrow_mix_massign() (str, u32, str) = { + let a: str = "abc"; + let b: u32 = 7; + let c: str = "wxyz"; + return (a, b, c); +}; + +@test fn sret_narrow_mix_massign() void = { + let a: str = ""; + let b: u32 = 0; + let c: str = ""; + a, b, c = f_sret_narrow_mix_massign(); + assert(!(len(a) != 3)); + assert(!(b != 7)); + assert(!(len(c) != 4)); +}; + +fn second_t1_lit_packed() u32 = { + let t: (u32, u32) = (3, 4); + if (t.0 != 3) { return 99; }; + return t.1; +}; + +@test fn t1_lit_packed() void = { + assert(!(second_t1_lit_packed() != 4)); +}; + +@test fn t1_lit_16() void = { + let t: (i64, i64) = (3, 4); + assert(!(t.0 != 3)); + assert(!(t.1 != 4)); +}; + +@test fn t1_lit_mixed() void = { + let t: (i64, str) = (12, "wxyz"); + assert(!(t.0 != 12)); + assert(!(len(t.1) != 4)); +}; + +fn second_t1_lit_3scalar() i64 = { + let t: (i64, i64, i64) = (3, 4, 5); + if (t.0 != 3) { return -1; }; + if (t.1 != 4) { return -2; }; + return t.2; +}; + +@test fn t1_lit_3scalar() void = { + assert(!(second_t1_lit_3scalar() != 5)); +}; + +fn f_t1_call_3scalar() (i64, i64, i64) = { + return (7, 8, 9); +}; + +@test fn t1_call_3scalar() void = { + let t: (i64, i64, i64) = f_t1_call_3scalar(); + assert(!(t.0 != 7)); + assert(!(t.1 != 8)); + assert(!(t.2 != 9)); +}; + +fn f_t1_call_noannot_neutral() (i64, i64) = { + return (3, 4); +}; + +@test fn t1_call_noannot_neutral() void = { + let t = f_t1_call_noannot_neutral(); + assert(!(t.0 != 3)); + assert(!(t.1 != 4)); +}; + +fn pick_t2_param_packed(t: (u32, u32), k: i32) u32 = { + if (k == 0) { return t.0; }; + return t.1; +}; + +@test fn t2_param_packed() void = { + let t: (u32, u32) = (41, 17); + assert(!(pick_t2_param_packed(t, 0) != 41)); + assert(!(pick_t2_param_packed(t, 1) != 17)); +}; + +fn pick_t2_param_16(t: (i64, i64), k: i32) i64 = { + if (k == 0) { return t.0; }; + return t.1; +}; + +@test fn t2_param_16() void = { + let t: (i64, i64) = (41, 17); + assert(!(pick_t2_param_16(t, 0) != 41)); + assert(!(pick_t2_param_16(t, 1) != 17)); +}; + +fn g_t2_mixed_order(a: i64, t: (u32, u32), b: i64) i64 = { + return a*1000 + (t.0: i64)*100 + (t.1: i64)*10 + b; +}; + +fn h_t2_mixed_order(t: (i64, i64), s: str, k: i64) i64 = { + return t.0 + t.1 + (len(s): i64) + k; +}; + +@test fn t2_mixed_order() void = { + let t: (u32, u32) = (3, 4); + assert(!(g_t2_mixed_order(1, t, 2) != 1342)); + let u: (i64, i64) = (10, 20); + assert(!(h_t2_mixed_order(u, "abc", 7) != 40)); +}; + +fn pick_t2_lit_arg(t: (i64, i64), k: i32) i64 = { + if (k == 0) { return t.0; }; + return t.1; +}; + +@test fn t2_lit_arg() void = { + assert(!(pick_t2_lit_arg((41, 17), 0) != 41)); + assert(!(pick_t2_lit_arg((41, 17), 1) != 17)); +}; + +fn fsum_t2_float_param(t: (f64, i64)) f64 = { + return t.0 + (t.1: f64); +}; + +@test fn t2_float_param() void = { + let t: (f64, i64) = (2.5, 4); + assert(!(fsum_t2_float_param(t) != 6.5)); +}; + +fn mk_t2_unwrap_arg() ((i64, i64) | nomem) = { + let a: i64 = 5; + let b: i64 = 6; + return (a, b); +}; + +fn sum_t2_unwrap_arg(t: (i64, i64)) i64 = { + return t.0 + t.1; +}; + +@test fn t2_unwrap_arg() void = { + assert(!(sum_t2_unwrap_arg(mk_t2_unwrap_arg()!) != 11)); +}; + +fn f_t2_gp_pressure(a: i64, b: i64, c: i64, d: i64, t: (i64, i64), e: i64) i64 = { + if (e != 60) { return -1; }; + if (a != 1 || b != 2 || c != 3 || d != 4) { return -2; }; + return t.0 * 100 + t.1; +}; + +@test fn t2_gp_pressure() void = { + let t: (i64, i64) = (7, 9); + assert(!(f_t2_gp_pressure(1, 2, 3, 4, t, 60) != 709)); +}; + +fn g_t2_variadic_after_tuple(t: (i64, i64), xs: i64...) i64 = { + let s: i64 = t.0 + t.1; + for (let x .. xs) { s += x; }; + return s; +}; + +@test fn t2_variadic_after_tuple() void = { + let t: (i64, i64) = (3, 4); + assert(!(g_t2_variadic_after_tuple(t, 1, 2) != 10)); +}; + +fn first_t2_variadic_of_tuples(ts: (i64, i64)...) i64 = { + if (len(ts) == 0) { return -1; }; + return ts[0].0; +}; + +@test fn t2_variadic_of_tuples() void = { + let t: (i64, i64) = (3, 4); + assert(!(first_t2_variadic_of_tuples(t) != 3)); +}; + +let g_t3_global_elem: (str, i64) = ("hello", 9); +let h_t3_global_elem: (u32, u32) = (5, 6); + +@test fn t3_global_elem() void = { + assert(!(len(g_t3_global_elem.0) != 5)); + assert(!(g_t3_global_elem.1 != 9)); + assert(!(h_t3_global_elem.0 != 5)); + assert(!(h_t3_global_elem.1 != 6)); +}; + +fn take_fc_arg_deref_16(e: (void | size)) size = { + if (!(e is size)) { return 99; }; + return e as size; +}; + +@test fn fc_arg_deref_16() void = { + let v: (void | size) = 7: size; + let p: *(void | size) = &v; + assert(!(take_fc_arg_deref_16(*p) != 7)); +}; + +type un16_fc_deref_let_match_16 = (void | size); + +@test fn fc_deref_let_match_16() void = { + let v: un16_fc_deref_let_match_16 = 5: size; + let p: *un16_fc_deref_let_match_16 = &v; + let w = *p; + assert(!(!(w is size))); + match (*p) { + case let s: size => { assert(!(s != 5)); }; + case void => { assert(false); }; + }; +}; + +type un32_fc_deref_let_match_32str = (void | str); + +@test fn fc_deref_let_match_32str() void = { + let v: un32_fc_deref_let_match_32str = "atcap"; + let p: *un32_fc_deref_let_match_32str = &v; + let w = *p; + assert(!(!(w is str))); + match (*p) { + case let s: str => { assert(!(s.len != 5)); }; + case void => { assert(false); }; + }; +}; + +type un16_fc_deref_asg_16 = (void | size); + +@test fn fc_deref_asg_16() void = { + let v: un16_fc_deref_asg_16 = 5: size; + let p: *un16_fc_deref_asg_16 = &v; + let w: un16_fc_deref_asg_16 = void; + w = *p; + assert(!(!(w is size))); + assert(!((w as size) != 5)); +}; + +type un16_fc_deref_ret_16 = (void | size); + +fn get_fc_deref_ret_16(p: *un16_fc_deref_ret_16) un16_fc_deref_ret_16 = { + return *p; +}; + +@test fn fc_deref_ret_16() void = { + let v: un16_fc_deref_ret_16 = 5: size; + let w = get_fc_deref_ret_16(&v); + assert(!(!(w is size))); + assert(!((w as size) != 5)); +}; + +type un16_fc_deref_is_as_16 = (void | size); + +@test fn fc_deref_is_as_16() void = { + let v: un16_fc_deref_is_as_16 = 5: size; + let p: *un16_fc_deref_is_as_16 = &v; + assert(!(!((*p) is size))); + assert(!(((*p) as size) != 5)); +}; + +type un32_fc_deref_as_32str = (void | str); + +@test fn fc_deref_as_32str() void = { + let v: un32_fc_deref_as_32str = "atcap"; + let p: *un32_fc_deref_as_32str = &v; + let s = (*p) as str; + assert(!(s.len != 5)); +}; + +type un32_fc_deref_arg_32str = (void | str); + +fn take_fc_deref_arg_32str(e: un32_fc_deref_arg_32str) i32 = { + if (!(e is str)) { return 99; }; + let s = e as str; + if (s.len != 5) { return 98; }; + return 0; +}; + +@test fn fc_deref_arg_32str() void = { + let v: un32_fc_deref_arg_32str = "atcap"; + let p: *un32_fc_deref_arg_32str = &v; + assert(!(take_fc_deref_arg_32str(*p) != 0)); +}; + +type un16_fc_deref_widen_16to24 = (void | size); +type un3_fc_deref_widen_16to24 = (void | size | str); + +@test fn fc_deref_widen_16to24() void = { + let v: un16_fc_deref_widen_16to24 = 5: size; + let p: *un16_fc_deref_widen_16to24 = &v; + let w: un3_fc_deref_widen_16to24 = *p; + assert(!(!(w is size))); + assert(!((w as size) != 5)); +}; + +type s2_fc_deref_struct24_neighbor = struct { a: size, b: size }; +type un24_fc_deref_struct24_neighbor = (void | s2_fc_deref_struct24_neighbor); + +@test fn fc_deref_struct24_neighbor() void = { + let lo: size = 111; + let v: un24_fc_deref_struct24_neighbor = s2_fc_deref_struct24_neighbor { a = 5, b = 6 }; + let hi: size = 222; + let p: *un24_fc_deref_struct24_neighbor = &v; + let w = *p; + assert(!(!(w is s2_fc_deref_struct24_neighbor))); + match (w) { + case let s: s2_fc_deref_struct24_neighbor => { assert(!(s.a != 5 || s.b != 6)); }; + case void => { assert(false); }; + }; + assert(!(lo != 111)); + assert(!(hi != 222)); +}; + +type big_fc_deref_56_memread = struct { a: u64, b: u64, c: u64, d: u64, e: u64, f: u64 }; +type un56_fc_deref_56_memread = (void | big_fc_deref_56_memread); + +@test fn fc_deref_56_memread() void = { + let v: un56_fc_deref_56_memread = big_fc_deref_56_memread { a = 1u64, b = 2u64, c = 3u64, d = 4u64, e = 5u64, f = 6u64 }; + let p: *un56_fc_deref_56_memread = &v; + let w = *p; + assert(!(!(w is big_fc_deref_56_memread))); + match (*p) { + case let s: big_fc_deref_56_memread => { assert(!(s.a != 1u64 || s.f != 6u64)); }; + case void => { assert(false); }; + }; +}; + +type un16_fc_cast_ident_arg = (void | size); + +fn take_fc_cast_ident_arg(e: un16_fc_cast_ident_arg) size = { + if (!(e is size)) { return 99; }; + return e as size; +}; + +@test fn fc_cast_ident_arg() void = { + let v: un16_fc_cast_ident_arg = 7: size; + assert(!(take_fc_cast_ident_arg((v: un16_fc_cast_ident_arg)) != 7)); +}; + +type un16_fc_cast_widen_let = (void | size); +type un3_fc_cast_widen_let = (void | size | str); + +@test fn fc_cast_widen_let() void = { + let v: un16_fc_cast_widen_let = 5: size; + let w: un3_fc_cast_widen_let = (v: un3_fc_cast_widen_let); + assert(!(!(w is size))); + assert(!((w as size) != 5)); +}; + +type un16_fc_unwrap_tagged_let = (void | size); +type err_fc_unwrap_tagged_let = !void; + +fn get_fc_unwrap_tagged_let() (un16_fc_unwrap_tagged_let | err_fc_unwrap_tagged_let) = { + let v: un16_fc_unwrap_tagged_let = 7: size; + return v; +}; + +fn use_fc_unwrap_tagged_let() (size | err_fc_unwrap_tagged_let) = { + let w = get_fc_unwrap_tagged_let()?; + if (!(w is size)) { return 99; }; + return w as size; +}; + +@test fn fc_unwrap_tagged_let() void = { + match (use_fc_unwrap_tagged_let()) { + case let s: size => { assert(!(s != 7)); }; + case err_fc_unwrap_tagged_let => { assert(false); }; + }; +}; + +type err_fc_unwrap_ident = !void; + +fn use_fc_unwrap_ident(v: (size | err_fc_unwrap_ident)) (size | err_fc_unwrap_ident) = { + let w = v?; + return w: size; +}; + +@test fn fc_unwrap_ident() void = { + let v: (size | err_fc_unwrap_ident) = 7: size; + match (use_fc_unwrap_ident(v)) { + case let s: size => { assert(!(s != 7)); }; + case err_fc_unwrap_ident => { assert(false); }; + }; +}; + +type err_fc_unwrap_deref = !void; + +fn use_fc_unwrap_deref(p: *(size | err_fc_unwrap_deref)) (size | err_fc_unwrap_deref) = { + let x = (*p)?; + return x: size; +}; + +@test fn fc_unwrap_deref() void = { + let v: (size | err_fc_unwrap_deref) = 7: size; + match (use_fc_unwrap_deref(&v)) { + case let s: size => { assert(!(s != 7)); }; + case err_fc_unwrap_deref => { assert(false); }; + }; +}; + +type un16_fc_cast_deref_arg = (void | size); + +fn take_fc_cast_deref_arg(e: un16_fc_cast_deref_arg) size = { + if (!(e is size)) { return 99; }; + return e as size; +}; + +@test fn fc_cast_deref_arg() void = { + let v: un16_fc_cast_deref_arg = 7: size; + let p: *un16_fc_cast_deref_arg = &v; + assert(!(take_fc_cast_deref_arg(((*p): un16_fc_cast_deref_arg)) != 7)); +}; + +type un16_fc_deref_void_16 = (void | size); + +@test fn fc_deref_void_16() void = { + let v: un16_fc_deref_void_16 = void; + let p: *un16_fc_deref_void_16 = &v; + match (*p) { + case let s: size => { assert(false); }; + case void => { }; + }; + assert(!((*p) is size)); + let w = *p; + assert(!(!(w is void))); +}; + +type un16_fc_deref_slice_elem_16 = (void | size); + +@test fn fc_deref_slice_elem_16() void = { + let xs: []un16_fc_deref_slice_elem_16 = []; + append(xs, 11: size); + append(xs, void); + let p: *un16_fc_deref_slice_elem_16 = &xs[0]; + match (*p) { + case let s: size => { assert(!(s != 11)); }; + case void => { assert(false); }; + }; + let q: *un16_fc_deref_slice_elem_16 = &xs[1]; + assert(!(!((*q) is void))); +}; + +type big4_fc_deref_arg_40 = struct { a: u64, b: u64, c: u64, d: u64 }; +type un40_fc_deref_arg_40 = (void | big4_fc_deref_arg_40); + +fn take_fc_deref_arg_40(e: un40_fc_deref_arg_40) u64 = { + match (e) { + case let g: big4_fc_deref_arg_40 => { + if (g.b != 2u64 || g.c != 3u64) { return 90u64; }; + return g.a + g.d; + }; + case void => { return 0u64; }; + }; +}; + +@test fn fc_deref_arg_40() void = { + let v: un40_fc_deref_arg_40 = big4_fc_deref_arg_40 { a = 30u64, b = 2u64, c = 3u64, d = 12u64 }; + let p: *un40_fc_deref_arg_40 = &v; + assert(!(take_fc_deref_arg_40(*p) != 42u64)); +}; + +type big_fc_deref_arg_56 = struct { a: u64, b: u64, c: u64, d: u64, e: u64, f: u64 }; +type un56_fc_deref_arg_56 = (void | big_fc_deref_arg_56); + +fn take_fc_deref_arg_56(e: un56_fc_deref_arg_56) u64 = { + match (e) { + case let g: big_fc_deref_arg_56 => { + if (g.b != 2u64 || g.e != 5u64) { return 90u64; }; + return g.a + g.f; + }; + case void => { return 0u64; }; + }; +}; + +@test fn fc_deref_arg_56() void = { + let v: un56_fc_deref_arg_56 = big_fc_deref_arg_56 { a = 30u64, b = 2u64, c = 3u64, d = 4u64, e = 5u64, f = 12u64 }; + let p: *un56_fc_deref_arg_56 = &v; + assert(!(take_fc_deref_arg_56(*p) != 42u64)); +}; + +type un16_fc_arg_balance_multi = (void | size); + +fn take_fc_arg_balance_multi(a: size, e: un16_fc_arg_balance_multi, b: size) size = { + if (a != 100) { return 90; }; + if (!(e is size)) { return 91; }; + if ((e as size) != 7) { return 92; }; + if (b != 200) { return 93; }; + return a + (e as size) + b; +}; + +@test fn fc_arg_balance_multi() void = { + let v: un16_fc_arg_balance_multi = 7: size; + let p: *un16_fc_arg_balance_multi = &v; + assert(!(take_fc_arg_balance_multi(100, *p, 200) != 307)); + assert(!(take_fc_arg_balance_multi(100, *p, 200) != 307)); +}; + +@test fn t44_inferred_let_cast() void = { + let t = (4: size, 2: size); + assert(!(t.0 != 4)); + assert(!(t.1 != 2)); +}; + +@test fn t44_inferred_let_bare() void = { + let t = (4, 2); + assert(!(t.0 != 4)); + assert(!(t.1 != 2)); +}; + +@test fn t44_inferred_let_mixed3() void = { + let t = (4: size, 2, 36: size); + assert(!(t.0 + t.2 != 40)); + assert(!(t.1 != 2)); +}; + +@test fn t44_inferred_let_float() void = { + let t = (1.5, 2.5); + assert(!(t.0 + t.1 != 4.0)); +}; + +@test fn t44_inferred_destructure() void = { + let t = (40: size, 2: size); + let (a, b) = t; + assert(!(a + b != 42)); +}; + +fn add_t44_inferred_arg(t: (size, size)) size = { + return t.0 + t.1; +}; + +@test fn t44_inferred_arg() void = { + let t = (40: size, 2: size); + assert(!(add_t44_inferred_arg(t) != 42)); +}; + +type un16_t57_ret_cast_elem = (void | size); + +fn mk_t57_ret_cast_elem() (un16_t57_ret_cast_elem, size) = { + return (5: size, 9); +}; + +@test fn t57_ret_cast_elem() void = { + let t = mk_t57_ret_cast_elem(); + assert(!(t.1 != 9)); + let (a, b) = mk_t57_ret_cast_elem(); + assert(!(!(a is size))); + assert(!(a as size != 5)); + assert(!(b != 9)); +}; + +fn mk_t57_ret_inline_union() ((void | size), size) = { + return (5: size, 9); +}; + +@test fn t57_ret_inline_union() void = { + let (a, b) = mk_t57_ret_inline_union(); + assert(!(!(a is size))); + assert(!(b != 9)); + assert(!(a as size != 5)); +}; + +type un16_t57_let_literal = (void | size); + +@test fn t57_let_literal() void = { + let t: (un16_t57_let_literal, size) = (5: size, 9); + assert(!(t.1 != 9)); + assert(!(!(t.0 is size))); + assert(!(t.0 as size != 5)); +}; + +type un16_t57_tagged_second_elem = (void | size); + +fn mk2_t57_tagged_second_elem() (size, un16_t57_tagged_second_elem) = { + return (3, 7: size); +}; + +@test fn t57_tagged_second_elem() void = { + let (a, b) = mk2_t57_tagged_second_elem(); + assert(!(a != 3)); + assert(!(!(b is size))); + assert(!(b as size != 7)); +}; + +type unf_t57_float_payload = (void | f64); + +fn mkf_t57_float_payload() (unf_t57_float_payload, size) = { + return (1.5, 9); +}; + +@test fn t57_float_payload() void = { + let (a, b) = mkf_t57_float_payload(); + assert(!(b != 9)); + assert(!(!(a is f64))); + assert(!(a as f64 != 1.5)); +}; + +type un_t57_tagged_mid_elem = (void | size); + +fn mk3_t57_tagged_mid_elem() (size, un_t57_tagged_mid_elem, size) = { + return (1, 5: size, 9); +}; + +@test fn t57_tagged_mid_elem() void = { + let (a, b, c) = mk3_t57_tagged_mid_elem(); + assert(!(a != 1)); + assert(!(!(b is size))); + assert(!(b as size != 5)); + assert(!(c != 9)); +}; + +type un_t57_two_tagged_void_elem = (void | size); + +fn mk_t57_two_tagged_void_elem() (un_t57_two_tagged_void_elem, un_t57_two_tagged_void_elem) = { + return (5: size, void); +}; + +@test fn t57_two_tagged_void_elem() void = { + let t = mk_t57_two_tagged_void_elem(); + assert(!(!(t.0 is size))); + assert(!(t.0 as size != 5)); + assert(!(!(t.1 is void))); +}; + +type un_t57_sse_coexist = (void | size); + +fn mk_t57_sse_coexist() (f64, un_t57_sse_coexist) = { + return (0.5, 7: size); +}; + +@test fn t57_sse_coexist() void = { + let (x, e) = mk_t57_sse_coexist(); + assert(!(x * 4.0 != 2.0)); + assert(!(!(e is size))); + assert(!(e as size != 7)); +}; + +type un16_t57_bare_untyped_payload = (void | size); + +fn mkb_t57_bare_untyped_payload() (un16_t57_bare_untyped_payload, size) = { + return (5, 9); +}; + +@test fn t57_bare_untyped_payload() void = { + let (a, b) = mkb_t57_bare_untyped_payload(); + assert(!(!(a is size))); + assert(!(a as size != 5)); + assert(!(b != 9)); +}; + +type un16_t57_ident_no_regress = (void | size); + +fn mk_t57_ident_no_regress() (un16_t57_ident_no_regress, size) = { + let e: un16_t57_ident_no_regress = 5: size; + return (e, 9); +}; + +@test fn t57_ident_no_regress() void = { + let t = mk_t57_ident_no_regress(); + assert(!(t.1 != 9)); + assert(!(!(t.0 is size))); + assert(!(t.0 as size != 5)); +}; diff --git a/test/lang/tuple_tagged_union_test.ww b/test/lang/tuple_tagged_union_test.ww new file mode 100644 index 00000000..83a78603 --- /dev/null +++ b/test/lang/tuple_tagged_union_test.ww @@ -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); +}; diff --git a/test/lang/tupparam_test.ww b/test/lang/tupparam_test.ww new file mode 100644 index 00000000..d34f20da --- /dev/null +++ b/test/lang/tupparam_test.ww @@ -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)); +}; diff --git a/test/lang/tuprecv_test.ww b/test/lang/tuprecv_test.ww new file mode 100644 index 00000000..238017dd --- /dev/null +++ b/test/lang/tuprecv_test.ww @@ -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); +}; diff --git a/test/wcc/905_tupparam_run.c b/test/wcc/905_tupparam_run.c deleted file mode 100644 index dfc7f4d9..00000000 --- a/test/wcc/905_tupparam_run.c +++ /dev/null @@ -1,308 +0,0 @@ -/* - * 905_tupparam_run — runtime + byte-id net for #163, the tuple-PARAM ABI - * (the param twin of #164's tuple RETURN). - * - * THE BUG (#163, LIVE drop on master): a tuple passed AS AN ARGUMENT was - * unhandled in BOTH stages — no tuple arm in the cgcall arg push, the - * cgcall arg pop, OR the callee cgfnparams receive. A tuple-typed call - * result (`f(g())` where g returns a tuple) left its elements in the - * return-ABI cursor (AX/DX/CX/R8 + X0/X1, per #164); the SEND fell to the - * 1-GP-word `else` (PUSHQ AX / POPQ DI) so ALL BUT THE FIRST ELEMENT was - * dropped, and the callee read its tuple param as a single GP word. This - * broke INTEGER tuple params too; float elements were doubly lost (they - * ride X0/X1, never AX). - * - * THE FIX: per-element SysV class placement reusing #164's helper. SEND — - * cgexpr leaves the tuple in the return cursor; restage it into a frame - * slot (@tupargscr) by class via tuple_store/tupstore, then push the slot - * words high->low so the pop drains slot+0 first into the SysV ARG cursor - * (DI/SI/.. + X0..X7). The frame slot decouples the return-class regs - * (which OVERLAP the arg-class regs) from the arg placement. RECV — the - * callee walks the tuple's elements over the arg cursor, storing each into - * its frame slot positionally. Symmetric across cstage (cmd/w6c/cgen.c) - * and wwstage (cgenutil.ww pushargsrev + cgenexpr.ww cgcall pop + - * cgendecl.ww cgfnparams). - * - * SCOPE: register-class tuple ARGS produced by a CALL (the only form that - * materialises a tuple value today — `let t = (1,2)` as a first-class - * value is a separate unimplemented gap, so the SEND scopes to the N_CALL - * producer and never pushes stale regs, rule 7). Arg-register overflow - * loud-stops (the partial-spill stitch is out of scope, twin of #164's - * cap); the loudstop row asserts BOTH stages ERROR. - * - * GATE-BLIND TO BYTE-ID ALONE: the bootstrap passes no tuple params, and - * pre-fix both stages were symmetric-WRONG (both PUSHQ AX), so the cs==ww - * .s gate HOLDS on master for the value rows — they diverge only at - * RUNTIME. Each value row carries BOTH dimensions (modelled on 956): - * (a) cstage `ww build` + run, asserting the exit code (catches #163: - * master returns the wrong exit / dropped element). - * (b) w6c vs w6c_ww `.s` cmp (rule-10: both stages fixed identically). - */ -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { - const char *label; - const char *src; - int want_exit; - int want_compile_fail; /* loud-stop rows must NOT compile */ -}; - -static const struct row rows[] = { - /* 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; t.0+t.1 != 8.0 -> return 1. Post-fix each float rides the - * SSE arg cursor (X0,X1). */ - { "f64f64_arg", - "package main;\n" - "fn pair(a: f64, b: f64) (f64, f64) = { return (a, b); };\n" - "fn add(t: (f64, f64)) f64 = { return t.0 + t.1; };\n" - "export fn main() i32 = {\n" - "\tif (add(pair(3.0, 5.0)) != 8.0) { return 1; };\n" - "\treturn 0;\n" - "};\n", 0 }, - /* (i64, i64) arg — proves the broader INTEGER-tuple-param drop is - * fixed (master dropped the second i64 too). e0->DI, e1->SI. */ - { "i64i64_arg", - "package main;\n" - "fn pair(a: i64, b: i64) (i64, i64) = { return (a, b); };\n" - "fn add(t: (i64, i64)) i64 = { return t.0 + t.1; };\n" - "export fn main() i32 = {\n" - "\tif (add(pair(3, 5)) != 8) { return 1; };\n" - "\treturn 0;\n" - "};\n", 0 }, - /* (f64, i64) — class independent of position: f64@X0 (SSE cursor), - * i64@DI (INTEGER cursor), independent counters. */ - { "f64i64_arg", - "package main;\n" - "fn mk(a: f64, b: i64) (f64, i64) = { return (a, b); };\n" - "fn add(t: (f64, i64)) i64 = { return (t.0: i64) + t.1; };\n" - "export fn main() i32 = {\n" - "\tif (add(mk(3.0, 5)) != 8) { return 1; };\n" - "\treturn 0;\n" - "};\n", 0 }, - /* (i64, f64) — order-swap: i64@DI, f64@X0. Confirms the float lands - * in the next XMM regardless of its positional slot. */ - { "i64f64_arg", - "package main;\n" - "fn mk(a: i64, b: f64) (i64, f64) = { return (a, b); };\n" - "fn add(t: (i64, f64)) i64 = { return t.0 + (t.1: i64); };\n" - "export fn main() i32 = {\n" - "\tif (add(mk(3, 5.0)) != 8) { return 1; };\n" - "\treturn 0;\n" - "};\n", 0 }, - /* (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. */ - { "f64str_arg", - "package main;\n" - "fn mk(a: f64) (f64, str) = { return (a, \"hello\"); };\n" - "fn add(t: (f64, str)) i64 = {\n" - "\treturn (t.0: i64) + (t.1.len: i64);\n" - "};\n" - "export fn main() i32 = {\n" - "\tif (add(mk(4.0)) != 9) { return 1; };\n" - "\treturn 0;\n" - "};\n", 0 }, - /* 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: pushargsrev evals right-to-left, so - * h() restages into the slot + drains it to the stack BEFORE g() - * restages into the SAME slot (h's words already pushed, safe to - * overwrite). Drain forward: s->DI,SI; t->DX,CX. 1+2+3+4 = 10. A - * collision (both restaged before either pushed) would corrupt the - * first-pushed tuple's words. */ - { "two_tuple_args", - "package main;\n" - "fn pair(a: i64, b: i64) (i64, i64) = { return (a, b); };\n" - "fn add4(s: (i64, i64), t: (i64, i64)) i64 = {\n" - "\treturn s.0 + s.1 + t.0 + t.1;\n" - "};\n" - "export fn main() i32 = {\n" - "\tif (add4(pair(1, 2), pair(3, 4)) != 10) { return 1; };\n" - "\treturn 0;\n" - "};\n", 0 }, - /* CONTROL — a tuple arg threaded through a chain of two calls, - * proving the SEND/RECV round-trips through the slot intact. */ - { "f64f64_chain", - "package main;\n" - "fn pair(a: f64, b: f64) (f64, f64) = { return (a, b); };\n" - "fn id(t: (f64, f64)) f64 = { return t.0 * 10.0 + t.1; };\n" - "export fn main() i32 = {\n" - "\tif (id(pair(3.0, 5.0)) != 35.0) { return 1; };\n" - "\treturn 0;\n" - "};\n", 0 }, - /* LOUD-STOP — 5 i64 scalars + an (i64,i64) tuple = 7 INTEGER arg - * eightbytes, overflowing the 6 GP arg regs (DI/SI/DX/CX/R8/R9). - * The partial-spill stitch is out of scope (twin of #164's cap), so - * BOTH stages must FAIL TO COMPILE (rule 7: surface, never silently - * drop). Master has no tuple-arg arm (pushes the tuple as 1 word -> - * 6 GP, no overflow) and builds the miscompile, so want_compile_fail - * discriminates. */ - { "gp_overflow_loudstop", - "package main;\n" - "fn pair(a: i64, b: i64) (i64, i64) = { return (a, b); };\n" - "fn f(a: i64, b: i64, c: i64, d: i64, e: i64, t: (i64, i64)) i64 = {\n" - "\treturn a + b + c + d + e + t.0 + t.1;\n" - "};\n" - "export fn main() i32 = {\n" - "\treturn (f(1, 2, 3, 4, 5, pair(6, 7)): i32);\n" - "};\n", 0, 1 }, - { NULL, NULL, 0, 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, "tupparam: w6c_ww missing — cannot run the " - "cs==ww byte-id gate\n"); - return 1; - } - - int n = 0, fail = 0; - for (int i = 0; rows[i].src; i++, n++) { - char tmpdir[64], src[128], outbin[128], rmcmd[160]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwtupp_%d_d_%d", - getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/wwtupp_%d_%d.ww", - tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/wwtupp_%d_%d", - tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - FILE *f = fopen(src, "wb"); - if (f == NULL) { fail++; runwait(rmcmd); continue; } - fputs(rows[i].src, f); - fclose(f); - - char cmd[2048]; - - /* LOUD-STOP rows: the arg-reg overflow must FAIL TO COMPILE in - * BOTH stages (rule 7). Assert (a) cstage `ww build` errors and - * (b) w6c AND w6c_ww each return non-zero. No .s is produced, so - * the byte-id cmp is skipped. */ - if (rows[i].want_compile_fail) { - snprintf(cmd, sizeof cmd, - "%s/ww build -o %s %s >/dev/null 2>&1", - bin, outbin, src); - if (runwait(cmd) == 0) { - fprintf(stderr, "row[%s]: cstage build SUCCEEDED, " - "want loud-stop (arg-reg overflow)\n", - rows[i].label); - fail++; - } - snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>/dev/null", - w6c, src); - if (runwait(cmd) == 0) { - fprintf(stderr, "row[%s]: w6c emitted .s, want " - "loud-stop\n", rows[i].label); - fail++; - } - snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>/dev/null", - w6c_ww, src); - if (runwait(cmd) == 0) { - fprintf(stderr, "row[%s]: w6c_ww emitted .s, want " - "loud-stop\n", rows[i].label); - fail++; - } - runwait(rmcmd); - continue; - } - - /* (a) cstage build + run in a scratch dir. */ - 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++; - } - - /* (b) cs==ww byte-id gate: emit .s from both stages, cmp. */ - char cs_s[160], ws_s[160]; - snprintf(cs_s, sizeof cs_s, "%s/wwtupp_%d_%d_cs.s", - tmpdir, getpid(), i); - snprintf(ws_s, sizeof ws_s, "%s/wwtupp_%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 tuple-param tests failed\n", fail, n); - return 1; - } - printf("tupparam: %d/%d ok (cstage run + cs==ww byte-id)\n", n, n); - return 0; -} diff --git a/test/wcc/936_tagged_tuple_widen_run.c b/test/wcc/936_tagged_tuple_widen_run.c deleted file mode 100644 index 1fc1c643..00000000 --- a/test/wcc/936_tagged_tuple_widen_run.c +++ /dev/null @@ -1,526 +0,0 @@ -/* - * 936_tagged_tuple_widen_run — #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 (it is not - * a widen-cast: its destination is the variant, not the union), fell - * to the SCALAR arm 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. - * - * row | shape | want - * -----------------------+--------------------------------------+----- - * cast_let_packed | let v: item = ((65,90): rng); .0/.1 | 0 - * cast_let_3member | + (str,*fn) member present, never | - * | constructed (layout-neutral pin) | 0 - * cast_append_local | append(xs, cast) → index → extract | 0 - * cast_append_indexplace | append(cs[len-1], cast), [][]item | 0 - * cast_append_derefplace | through *[][]item param (via_outer) | 0 - * cast_append_ptrfield | append(p.cs[len-1], cast) — ptr- | - * | FIELD-rooted place | 0 - * cast_ident_elems | (start_b, end_b) u32 LOCALS — the | - * | handle_bracket shape | 0 - * cast_float_elem | (u32, f64) — MOVSD element store | 0 - * cast_str_elem | (i64, str) — 3-word wide element | 0 - * cast_arg_direct | cast tuple as a DIRECT tagged ARG — | - * | cg_widen_tagged_push routes tuple | - * | srcs to the scratch store (the fast | - * | scalar push dropped slot 1+) | 0 - * bare_ident_arg | bare (a, b) typed idents as a tagged | - * | ARG — same push routing, tag from | - * | the typed tuple | 0 - * bare_ident_no_regress | bare (a, b) typed-ident return into | - * | tagged — the strconv parseint shape | 0 - * bare_literal_reject | bare (5, 6) literal-element tuple | - * | into tagged stays the #242/#241 loud | - * | stop (the cast form lifts it; the | - * | bare form must NOT silently change) | err - * tuple_ident_widen | tuple-typed IDENT into tagged — #116 | - * | addressable block-copy arm (was the | - * | #72 loud-stop); both words round-trip| 0 - * - * Every K_RUN row also asserts cstage/wwstage asm byte-id. NNN<950, - * self-contained (/tmp, no imports) — rule-14's selfhost-sibling race - * does not apply (941 precedent). - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -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[] = { - { "cast_let_packed", - "package main;\n" - "type lit = rune;\n" - "type rng = (u32, u32);\n" - "type item = (lit | rng);\n" - "export fn main() i32 = {\n" - " let v: item = ((65, 90): rng);\n" - " match (v) {\n" - " case let r: rng => {\n" - " if (r.0 != 65) { return 1; };\n" - " if (r.1 != 90) { return 2; };\n" - " };\n" - " case => return 3;\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* the real charset element: the (str,*fn) class member is PRESENT - * IN THE TYPE but never constructed — its presence must not alter - * the lit/range slot layout or walks (fold-4 PF1 caveat). */ - { "cast_let_3member", - "package main;\n" - "type lit = rune;\n" - "type rng = (u32, u32);\n" - "type cls = (str, *fn(c: rune) bool);\n" - "type item = (lit | rng | cls);\n" - "export fn main() i32 = {\n" - " let v: item = ((65, 90): rng);\n" - " match (v) {\n" - " case let r: rng => {\n" - " if (r.0 != 65) { return 1; };\n" - " if (r.1 != 90) { return 2; };\n" - " };\n" - " case => return 3;\n" - " };\n" - " let w: item = ('a': lit);\n" - " match (w) {\n" - " case let l: lit => { if ((l: rune) != 'a') { return 4; }; };\n" - " case => return 5;\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "cast_append_local", - "package main;\n" - "type lit = rune;\n" - "type rng = (u32, u32);\n" - "type item = (lit | rng);\n" - "export fn main() i32 = {\n" - " let xs: []item;\n" - " append(xs, ('a': lit));\n" - " append(xs, ((65, 90): rng));\n" - " if (len(xs) != 2) { return 1; };\n" - " let e: item = xs[1];\n" - " match (e) {\n" - " case let r: rng => {\n" - " if (r.0 != 65) { return 2; };\n" - " if (r.1 != 90) { return 3; };\n" - " };\n" - " case => return 4;\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* append place = indexed inner slice — the handle_bracket - * `append(charsets[len(charsets)-1], ...)` route. */ - { "cast_append_indexplace", - "package main;\n" - "type lit = rune;\n" - "type rng = (u32, u32);\n" - "type item = (lit | rng);\n" - "export fn main() i32 = {\n" - " let charsets: [][]item;\n" - " let empty: []item;\n" - " append(charsets, empty);\n" - " append(charsets[len(charsets) - 1], ((7, 9): rng));\n" - " if (len(charsets[0]) != 1) { return 1; };\n" - " let e: item = charsets[0][0];\n" - " match (e) {\n" - " case let r: rng => {\n" - " if (r.0 != 7) { return 2; };\n" - " if (r.1 != 9) { return 3; };\n" - " };\n" - " case => return 4;\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* append place behind a *[][]item param — the via_outer - * (pointer-rooted) widen route. */ - { "cast_append_derefplace", - "package main;\n" - "type lit = rune;\n" - "type rng = (u32, u32);\n" - "type item = (lit | rng);\n" - "fn addrange(cs: *[][]item, a: u32, b: u32) void = {\n" - " if (len(*cs) == 0) {\n" - " let empty: []item;\n" - " append(*cs, empty);\n" - " };\n" - " append((*cs)[len(*cs) - 1], ((a, b): rng));\n" - "};\n" - "export fn main() i32 = {\n" - " let charsets: [][]item;\n" - " addrange(&charsets, 5, 8);\n" - " if (len(charsets) != 1) { return 1; };\n" - " if (len(charsets[0]) != 1) { return 2; };\n" - " let e: item = charsets[0][0];\n" - " match (e) {\n" - " case let r: rng => {\n" - " if (r.0 != 5) { return 3; };\n" - " if (r.1 != 8) { return 4; };\n" - " };\n" - " case => return 5;\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* append place rooted at a ptr-FIELD ([][]item field through - * *struct) — the re.charsets-style place spelling. */ - { "cast_append_ptrfield", - "package main;\n" - "type lit = rune;\n" - "type rng = (u32, u32);\n" - "type item = (lit | rng);\n" - "type holder = struct { cs: [][]item, n: i64 };\n" - "export fn main() i32 = {\n" - " let h: holder = holder { n = 0, ... };\n" - " let p: *holder = &h;\n" - " let empty: []item;\n" - " append(p.cs, empty);\n" - " append(p.cs[len(p.cs) - 1], ((7, 9): rng));\n" - " if (len(h.cs[0]) != 1) { return 1; };\n" - " match (h.cs[0][0]) {\n" - " case let r: rng => {\n" - " if (r.0 != 7) { return 2; };\n" - " if (r.1 != 9) { return 3; };\n" - " };\n" - " case => return 4;\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* typed u32 LOCALS as the tuple elements — handle_bracket's - * exact `(start_b, end_b): charset_range_item` shape. */ - { "cast_ident_elems", - "package main;\n" - "type lit = rune;\n" - "type rng = (u32, u32);\n" - "type item = (lit | rng);\n" - "export fn main() i32 = {\n" - " let start_b: u32 = 97;\n" - " let end_b: u32 = 122;\n" - " let v: item = ((start_b, end_b): rng);\n" - " match (v) {\n" - " case let r: rng => {\n" - " if (r.0 != 97) { return 1; };\n" - " if (r.1 != 122) { return 2; };\n" - " };\n" - " case => return 3;\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* float element rides the MOVSD store branch under the cast form */ - { "cast_float_elem", - "package main;\n" - "type mf = (u32, f64);\n" - "type item = (rune | mf);\n" - "export fn main() i32 = {\n" - " let a: u32 = 9;\n" - " let x: f64 = 2.5;\n" - " let v: item = ((a, x): mf);\n" - " match (v) {\n" - " case let m: mf => {\n" - " if (m.0 != 9) { return 1; };\n" - " if (m.1 != 2.5) { return 2; };\n" - " };\n" - " case => return 3;\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* str element rides the 3-word wide branch under the cast form */ - { "cast_str_elem", - "package main;\n" - "type ms = (i64, str);\n" - "type item = (rune | ms);\n" - "export fn main() i32 = {\n" - " let n: i64 = 12;\n" - " let s: str = \"wxyz\";\n" - " let v: item = ((n, s): ms);\n" - " match (v) {\n" - " case let m: ms => {\n" - " if (m.0 != 12) { return 1; };\n" - " if (len(m.1) != 4) { return 2; };\n" - " };\n" - " case => return 3;\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* cast tuple passed DIRECTLY as a tagged call arg — pre-fix - * cg_widen_tagged_push's scalar fast path pushed word 0 only - * (and coerced an unresolved tag to 0); tuple-typed sources now - * route through the scratch store choke-point. */ - { "cast_arg_direct", - "package main;\n" - "type lit = rune;\n" - "type rng = (u32, u32);\n" - "type item = (lit | rng);\n" - "fn check(v: item) i32 = {\n" - " match (v) {\n" - " case let r: rng => {\n" - " if (r.0 != 65) { return 1; };\n" - " if (r.1 != 90) { return 2; };\n" - " return 0;\n" - " };\n" - " case => return 3;\n" - " };\n" - "};\n" - "export fn main() i32 = {\n" - " return check(((65, 90): rng));\n" - "};\n", 0, K_RUN, NULL }, - /* bare typed-ident tuple as a tagged call arg — the strconv - * `(neg, n)` shape at a CALL boundary; rides the same push - * routing, tag resolved from the typed tuple. */ - { "bare_ident_arg", - "package main;\n" - "type item = (rune | (i64, i64));\n" - "fn check(v: item) i32 = {\n" - " match (v) {\n" - " case let r: (i64, i64) => {\n" - " if (r.0 != 5) { return 1; };\n" - " if (r.1 != 6) { return 2; };\n" - " return 0;\n" - " };\n" - " case => return 3;\n" - " };\n" - "};\n" - "export fn main() i32 = {\n" - " let a: i64 = 5;\n" - " let b: i64 = 6;\n" - " return check((a, b));\n" - "};\n", 0, K_RUN, NULL }, - /* bare N_TUPLE of TYPED idents (the strconv parseint `(neg, n)` - * return shape) must keep working — no-regress on the bare arm. */ - { "bare_ident_no_regress", - "package main;\n" - "fn mk() ((i64, i64) | nomem) = {\n" - " let a: i64 = 5;\n" - " let b: i64 = 6;\n" - " return (a, b);\n" - "};\n" - "export fn main() i32 = {\n" - " match (mk()) {\n" - " case let t: (i64, i64) => {\n" - " if (t.0 != 5) { return 1; };\n" - " if (t.1 != 6) { return 2; };\n" - " };\n" - " case => return 3;\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* bare literal-element tuple into tagged stays the #242/#241 - * loud-stop — the cast form lifts it (the cast supplies the - * variant type), the bare form must not silently change. */ - { "bare_literal_reject", - "package main;\n" - "fn mk() ((i64, i64) | nomem) = {\n" - " return (5, 6);\n" - "};\n" - "export fn main() i32 = {\n" - " match (mk()) {\n" - " case let t: (i64, i64) => { if (t.0 != 5) { return 1; }; };\n" - " case => return 2;\n" - " };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "tuple-in-union variant tag unresolved" }, - /* #116 GRADUATION: a tuple-typed IDENT source widened into a tagged - * slot. Pre-#116 the widen choke-point had no arm for it and loud- - * stopped (the row pinned that loud); #116 added the addressable - * block-copy arm, so the IDENT source now boxes correctly — both - * payload words round-trip (r.0 AND r.1). Companion shapes (index, - * deref) + the runtime read-back live in 944_nonlit_tuple_widen_run. */ - { "tuple_ident_widen", - "package main;\n" - "type lit = rune;\n" - "type rng = (u32, u32);\n" - "type item = (lit | rng);\n" - "export fn main() i32 = {\n" - " let t: rng = ((65, 90): rng);\n" - " let v: item = t;\n" - " match (v) {\n" - " case let r: rng => {\n" - " if (r.0 != 65) { return 1; };\n" - " if (r.1 != 90) { return 3; };\n" - " };\n" - " case => return 2;\n" - " };\n" - " return 0;\n" - "};\n", 0, - K_RUN, NULL }, -}; - -/* 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[160], outbin[160], errf[160], rmcmd[176], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/ttw_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/ttw_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/ttw_%d_%d", tmpdir, getpid(), i); - snprintf(errf, sizeof errf, "%s/err", tmpdir); - 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/ttw_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/ttw_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/ttw_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, "tagged_tuple_widen: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("tagged_tuple_widen: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/940_mixed_scalar_tuple_sret_run.c b/test/wcc/940_mixed_scalar_tuple_sret_run.c deleted file mode 100644 index 2ad79c55..00000000 --- a/test/wcc/940_mixed_scalar_tuple_sret_run.c +++ /dev/null @@ -1,257 +0,0 @@ -/* - * 940_mixed_scalar_tuple_sret_run — 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. - * - * Two SILENT, gate-blind (no bootstrap mixed-scalar tuple) cs!=ww - * divergences met here: - * - * (a) callee SEND (cstage cgen.c N_RETURN over-cap-tuple arm): the packed - * element offset advanced by the LITERAL expression's type size, not - * the declared return-type element size. A bare int literal element is - * stamped TY_UNTYPED_INT (size 0), so `foff += e->type->size` added 0 - * for a leading scalar — the next element clobbered it at offset 0 and - * every trailing element packed 8 bytes low. wwstage already walked the - * return-type tuple (c.fnret.list) for the size, so the stages diverged - * on the callee frame's store offsets. Fix: size foff from cg_ret_type's - * tuple params (rule-13 type table), mirroring wwstage. - * - * (b) caller RECEIVE (wwstage cgenstmt.ww cglet N_TTUPLE arm): the register - * tuple-receive branch (designed for in-cap 16/32B tuples) had NO - * capacity gate, so a 56B over-cap tuple was received via AX/DX/CX/R8 - * (+ R8 fill) instead of from the sret dest the callee actually wrote. - * cstage gates that branch on `sz == 16 || sz == 32` and falls an - * over-cap tuple through to the sret receive. Fix: add the same size - * gate to wwstage. - * - * Each program assigns DISTINCT, checkable values to every element (a scalar - * read directly, a slice/str via len()), and returns 0 only when all read - * back correctly — so a wrong offset (either bug) yields a nonzero exit. - * Both an annotated and an inferred `let t` are covered (same receive path). - * - * 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 -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -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[] = { - /* leading scalar (the canonical #240 case), annotated let. */ - { "int_slice_str", - "package main;\n" - "fn mk() (int, []u8, str) = {\n" - " let b: []u8; b.len = 5; b.cap = 9;\n" - " let s: str = \"abcd\";\n" - " return (42, b, s);\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (int, []u8, str) = mk();\n" - " if (t.0 != 42) { return 1; };\n" - " if (len(t.1) != 5) { return 2; };\n" - " if (len(t.2) != 4) { return 3; };\n" - " return 0;\n" - "};\n", 0 }, - /* same shape, inferred `let t = mk();` — exercises the receive - * path that picks up the type from the callee return. */ - { "int_slice_str_inferred", - "package main;\n" - "fn mk() (int, []u8, str) = {\n" - " let b: []u8; b.len = 6; b.cap = 8;\n" - " let s: str = \"xyz\";\n" - " return (17, b, s);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " if (t.0 != 17) { return 1; };\n" - " if (len(t.1) != 6) { return 2; };\n" - " if (len(t.2) != 3) { return 3; };\n" - " return 0;\n" - "};\n", 0 }, - /* trailing scalar: foff must reach the last element's offset. */ - { "slice_str_int", - "package main;\n" - "fn mk() ([]u8, str, int) = {\n" - " let b: []u8; b.len = 7; b.cap = 9;\n" - " let s: str = \"hello\";\n" - " return (b, s, 99);\n" - "};\n" - "export fn main() i32 = {\n" - " let t: ([]u8, str, int) = mk();\n" - " if (len(t.0) != 7) { return 1; };\n" - " if (len(t.1) != 5) { return 2; };\n" - " if (t.2 != 99) { return 3; };\n" - " return 0;\n" - "};\n", 0 }, - /* scalar in the middle. */ - { "str_int_slice", - "package main;\n" - "fn mk() (str, int, []u8) = {\n" - " let s: str = \"abcde\";\n" - " let b: []u8; b.len = 3; b.cap = 4;\n" - " return (s, 77, b);\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (str, int, []u8) = mk();\n" - " if (len(t.0) != 5) { return 1; };\n" - " if (t.1 != 77) { return 2; };\n" - " if (len(t.2) != 3) { return 3; };\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 tmpdir[96], src[128], outbin[128], errf[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/mxst_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/mxst_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/mxst_%d_%d", tmpdir, getpid(), i); - snprintf(errf, sizeof errf, "%s/err", tmpdir); - 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/mxst_bi_%d_%d.ww", getpid(), i); - snprintf(cs_s, sizeof cs_s, "/tmp/mxst_bi_%d_%d_cs.s", getpid(), i); - snprintf(ws_s, sizeof ws_s, "/tmp/mxst_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 " - "(#240 mixed-scalar tuple sret-layout regression)\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, "mixed_scalar_tuple_sret: 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, "mixed_scalar_tuple_sret: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("mixed_scalar_tuple_sret: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/940_overcap_tuple_field_store_run.c b/test/wcc/940_overcap_tuple_field_store_run.c deleted file mode 100644 index 44a34e43..00000000 --- a/test/wcc/940_overcap_tuple_field_store_run.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * 940_overcap_tuple_field_store_run — project #234: storing the result of an - * over-capacity tuple-returning call (sret: > 4 GP / > 2 SSE eightbytes, - * e.g. ([]u8,[]u8) = 6 GP) into a struct field or an indexed lvalue. - * - * The receive end (project #10 Fold B) wired single-var-let / destructure / - * reassign / return-forward, but 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 (RDI) - * straight at the destination slot when it is a LOCAL (BP-relative), so the - * callee writes the WHOLE tuple there: - * - LOCAL struct field `s.f = wide();` dest = s.off + field.off. - * - INDEXED local array `arr[i] = wide();` with a CONSTANT index — dest - * = arr.off + idx*esz (the only indexed form whose - * dest is a static BP offset). - * Every other dest — via_ptr field (`p.f`), global field (`g.f`), runtime / - * slice / pointer index — needs a runtime RDI-pointer dest (deferred to - * #234-tail) and HARD-ERRORS LOUD, never a silent truncating store (rule 7). - * - * Both stages emit byte-identically (rule 10); the wwstage struct-field slot - * is correctly sized only with #237 underneath (a tuple struct field's slot - * width), committed first in this branch. - * - * Rows (cap != len in every wide element so a dropped len OR cap is caught): - * A indexed_local `arr[1] = wide()`, arr: [2]([]u8,[]u8). RUN both stages - * + cs==ww byte-id. 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. - * B struct_field `s.f = wide()`, S = struct { hdr:int, f:([]u8,[]u8) }. - * foff!=0 (after hdr) + hdr-uncorrupted check. RUN both - * stages + cs==ww byte-id, pointer readback `(&s.f):*int`. - * C via_ptr_be `p.f = wide()` (p:*S) — BUILDERR, loud #234-tail on - * BOTH drivers. - * D global_be `g.f = wide()` (g a struct global) — BUILDERR, loud. - * E rtindex_be `arr[idx()] = wide()` (runtime index) — BUILDERR, loud. - * F ndot_index_be `s.arr[1] = wide()` (N_DOT-base index, array field of a - * local) — BUILDERR, loud. Regression witness: this form - * shipped SILENT on wwstage when the N_INDEX arm keyed its - * entry on sretretsize(elemtn) — elemtn is a value node for - * an N_DOT base, so the gate returned 0 and fell through to - * the truncating store. Re-keying on callsretsize(rhs) - * (callee-return SSoT) fires the base-shape loud-stop. - * G chained_index_be `a[0][1] = wide()` (chained N_INDEX base) — same class, - * BUILDERR, loud on both stages. - * - * GATE POLARITY: A/B must build+run exit 0 on BOTH stages AND be cs==ww - * byte-identical; C/D/E must FAIL the build with the cited #234-tail - * diagnostic on BOTH stages (rule 7 — loud, not an incidental error). - * NNN<950, self-contained (/tmp, no imports), so rule-14's selfhost-sibling - * race does not apply (945/799 precedent). - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -static int -file_contains(const char *path, const char *needle) -{ - FILE *f = fopen(path, "rb"); - if (!f) return 0; - char buf[8192]; - size_t n = fread(buf, 1, sizeof buf - 1, f); - fclose(f); - buf[n] = '\0'; - return strstr(buf, needle) != 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), 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 */ - -struct row { const char *label; const char *src; int kind; int want; - const char *experr; }; - -static const struct row rows[] = { - { "indexed_local", - "package main;\n" - "fn wide() ([]u8, []u8) = {\n" - " let a: []u8; a.len = 1; a.cap = 5;\n" - " let b: []u8; b.len = 2; b.cap = 6;\n" - " return (a, b);\n" - "};\n" - "export fn main() i32 = {\n" - " let arr: [2]([]u8, []u8);\n" - " arr[1] = wide();\n" - " let p: *int = (&arr[1]): *int;\n" - " if (p[1] != 1) { return 1; };\n" - " if (p[2] != 5) { return 2; };\n" - " if (p[4] != 2) { return 3; };\n" - " if (p[5] != 6) { return 4; };\n" - " return 0;\n" - "};\n", K_RUN, 0, NULL }, - { "struct_field", - "package main;\n" - "fn wide() ([]u8, []u8) = {\n" - " let a: []u8; a.len = 1; a.cap = 5;\n" - " let b: []u8; b.len = 2; b.cap = 6;\n" - " return (a, b);\n" - "};\n" - "type S = struct { hdr: int, f: ([]u8, []u8) };\n" - "export fn main() i32 = {\n" - " let s: S;\n" - " s.hdr = 99;\n" - " s.f = wide();\n" - " if (s.hdr != 99) { return 9; };\n" - " let p: *int = (&s.f): *int;\n" - " if (p[1] != 1) { return 1; };\n" - " if (p[2] != 5) { return 2; };\n" - " if (p[4] != 2) { return 3; };\n" - " if (p[5] != 6) { return 4; };\n" - " return 0;\n" - "};\n", K_RUN, 0, NULL }, - { "via_ptr_be", - "package main;\n" - "fn wide() ([]u8, []u8) = {\n" - " let a: []u8; a.len = 1; let b: []u8; b.len = 2;\n" - " return (a, b);\n" - "};\n" - "type S = struct { f: ([]u8, []u8) };\n" - "export fn main() i32 = {\n" - " let s: S;\n" - " let p: *S = &s;\n" - " p.f = wide();\n" - " return 0;\n" - "};\n", K_BUILDERR, 0, "#234-tail" }, - { "global_be", - "package main;\n" - "fn wide() ([]u8, []u8) = {\n" - " let a: []u8; a.len = 1; let b: []u8; b.len = 2;\n" - " return (a, b);\n" - "};\n" - "type S = struct { f: ([]u8, []u8) };\n" - "let g: S;\n" - "export fn main() i32 = {\n" - " g.f = wide();\n" - " return 0;\n" - "};\n", K_BUILDERR, 0, "#234-tail" }, - { "rtindex_be", - "package main;\n" - "fn wide() ([]u8, []u8) = {\n" - " let a: []u8; a.len = 1; let b: []u8; b.len = 2;\n" - " return (a, b);\n" - "};\n" - "fn ix() int = { return 0; };\n" - "export fn main() i32 = {\n" - " let arr: [2]([]u8, []u8);\n" - " arr[ix()] = wide();\n" - " return 0;\n" - "};\n", K_BUILDERR, 0, "#234-tail" }, - /* N_DOT-base index `s.arr[1] = wide()`: array-field-of-local. The - * dest is a static BP offset but the N_INDEX arm only wires a plain - * N_IDENT base, so this is a deferred form — MUST loud-stop. Witness - * for the gap that shipped silently on wwstage when the arm was keyed - * on sretretsize(elemtn) (a value node for an N_DOT base → 0 → fell - * through to the truncating store); now keyed on callsretsize(rhs). */ - { "ndot_index_be", - "package main;\n" - "fn wide() ([]u8, []u8) = {\n" - " let a: []u8; a.len = 1; let b: []u8; b.len = 2;\n" - " return (a, b);\n" - "};\n" - "type S = struct { arr: [2]([]u8, []u8) };\n" - "export fn main() i32 = {\n" - " let s: S;\n" - " s.arr[1] = wide();\n" - " return 0;\n" - "};\n", K_BUILDERR, 0, "#234-tail" }, - /* Chained index `a[0][1] = wide()`: N_INDEX base, same deferred class - * — loud-stop on both stages. */ - { "chained_index_be", - "package main;\n" - "fn wide() ([]u8, []u8) = {\n" - " let a: []u8; a.len = 1; let b: []u8; b.len = 2;\n" - " return (a, b);\n" - "};\n" - "export fn main() i32 = {\n" - " let a: [2][2]([]u8, []u8);\n" - " a[0][1] = wide();\n" - " return 0;\n" - "};\n", K_BUILDERR, 0, "#234-tail" }, -}; - -/* 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/ocf_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/ocf_%d_%d.ww", tmpdir, getpid(), i); - snprintf(errf, sizeof errf, "%s/ocf_%d_e_%d", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/ocf_%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 (r->kind == K_BUILDERR) { - int ok = (brc != 0) - && (r->experr == NULL || file_contains(errf, r->experr)); - if (!ok) - fprintf(stderr, "row[%s]: %s expected loud #234-tail " - "builderr (brc=%d)\n", r->label, driver, 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) for a K_RUN row. */ -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/ocf_bi_%d_%d.ww", getpid(), i); - snprintf(cs_s, sizeof cs_s, "/tmp/ocf_bi_%d_%d_cs.s", getpid(), i); - snprintf(ws_s, sizeof ws_s, "/tmp/ocf_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, "overcap_tuple_field_store: 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++; - } - } - - /* cs==ww byte-id for the K_RUN rows. */ - if (access(w6c_ww, X_OK) == 0) { - for (int i = 0; i < n; i++) { - if (rows[i].kind != K_RUN) continue; - total++; - if (byteid(w6c, w6c_ww, &rows[i], i) != 0) fail++; - } - } - - if (fail) { - fprintf(stderr, "overcap_tuple_field_store: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("overcap_tuple_field_store: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/940_tuple_in_union_run.c b/test/wcc/940_tuple_in_union_run.c deleted file mode 100644 index ab5e12ea..00000000 --- a/test/wcc/940_tuple_in_union_run.c +++ /dev/null @@ -1,344 +0,0 @@ -/* - * 940_tuple_in_union_run — 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. BARE tuples already worked - * (#240/#83); only the union-wrapped variant was broken. - * - * Three SILENT, gate-blind (no bootstrap tuple-in-union) cs!=ww divergences - * met here, all on the `(neg, n)`-shape tuple Hare's strconv parseint returns - * (`((bool, u64) | invalid | overflow)`, stou.ha): - * - * (a) cstage CONSTRUCTION (cgen.c N_RETURN tagged arm): a tuple variant fell - * through the scalar shuffle, which ZEROED the whole value (MOVQ $0 to - * tag + payload) — the operands were never packed. Fix: route the tuple - * variant through the scratch-slot widen path (cg_widen_tagged_store's - * new TY_TUPLE arm), packing each element into the union payload + tag. - * - * (b) wwstage CHECKER (check.ww N_MLET): `let (a,b)=t` over a plain tuple - * ident (the match-bound union payload) left the un-annotated binders - * UNTYPED — the bin node reading them was untyped → asserttyped abort. - * The element-type distribution only fired for an N_CALL rhs. Fix: - * consume the rhs tuple type for ANY rhs (mirror cstage check.c:2017). - * - * (c) BOTH stages DESTRUCTURE (N_MLET / cgmlet): the register-cursor receive - * assumes the rhs left every element in AX/DX/CX (a call's tuple-return - * ABI). For a tuple IDENT cgexpr loads only word0->AX, so the 2nd binder - * read a STALE DX. Fix: copy each element from the ident's slot at the - * register-ABI 8B stride. - * - * Construction is correct at ANY variant position (the variant tag, not a - * default-0) — `tuple_tag1` and `tuple_after_int` place the tuple at index 1. - * - * SCOPE (rule 7 loud-stop, PINNED by the K_BUILDERR row): a tuple built - * from a BARE LITERAL element (cstage mis-types `true`/`false`/untyped `7` → - * tag unresolved, #241 literal-init family; wwstage types them but mirrors - * cstage's CONDITION down per rule 10) loud-stops in cgen on BOTH stages - * rather than silently miscompile. The old eightbyte-share loud-stop - * ((i32,i32,u64), #243) DISSOLVED with the slot-SSoT tuple layout (tuple - * arc C-t0) — that row graduated to K_RUN below. The K_RUN rows build - * their tuple from TYPED expressions — the supported, byte-identical shape. - * - * K_RUN rows: build+run exit 0 on BOTH drivers AND cs==ww byte-identical. - * K_BUILDERR rows: build FAILS with the #242 diagnostic on BOTH drivers. - * NNN<950, self-contained (/tmp, no imports), so rule-14's selfhost-sibling - * race does not apply (903/940/945 precedent). - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -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; -} - -static int -file_contains(const char *path, const char *needle) -{ - FILE *f = fopen(path, "rb"); - if (!f) return 0; - char buf[8192]; - size_t n = fread(buf, 1, sizeof buf - 1, f); - fclose(f); - buf[n] = '\0'; - return strstr(buf, needle) != NULL; -} - -#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 kind; int want; - const char *experr; }; - -static const struct row rows[] = { - /* canonical #242 case: (bool,u64) at variant 0, match-bind + - * destructure, both elements checked. */ - { "bool_u64", - "package main;\n" - "fn mku(x: u64, s: bool) ((bool, u64) | void) = { return (s, x); };\n" - "export fn main() i32 = {\n" - " match (mku(7u64, true)) {\n" - " case let t: (bool, u64) => {\n" - " let (sg, u) = t;\n" - " if (u != 7u64) { return 1; };\n" - " if (!sg) { return 2; };\n" - " return 0;\n" - " };\n" - " case void => { return 3; };\n" - " };\n" - " return 4;\n" - "};\n", K_RUN, 0, NULL }, - /* the void variant: bare `return;` packs the void tag, the match - * takes the void arm. The tuple branch still compiles (typed vars). */ - { "void_arm", - "package main;\n" - "fn mku(ok: bool, x: u64, s: bool) ((bool, u64) | void) = {\n" - " if (ok) { return (s, x); };\n" - " return;\n" - "};\n" - "export fn main() i32 = {\n" - " match (mku(false, 1u64, true)) {\n" - " case let t: (bool, u64) => { return 1; };\n" - " case void => { return 0; };\n" - " };\n" - " return 2;\n" - "};\n", K_RUN, 0, NULL }, - /* tuple at variant 1 (`(void | (bool,u64))`): the construct must emit - * tag 1, not a default 0 — and the match must dispatch to it. */ - { "tuple_tag1", - "package main;\n" - "fn mku(x: u64, s: bool) (void | (bool, u64)) = { return (s, x); };\n" - "export fn main() i32 = {\n" - " match (mku(9u64, true)) {\n" - " case void => { return 50; };\n" - " case let t: (bool, u64) => {\n" - " let (sg, u) = t;\n" - " if (u != 9u64) { return 1; };\n" - " if (!sg) { return 2; };\n" - " return 0;\n" - " };\n" - " };\n" - " return 4;\n" - "};\n", K_RUN, 0, NULL }, - /* tuple variant 1 of `(int | (bool,u64))`, selected at runtime, built - * from typed locals (the parseint shape: an int-or-tuple result). */ - { "tuple_after_int", - "package main;\n" - "fn mk(which: bool) (int | (bool, u64)) = {\n" - " let s: bool = true;\n" - " let v: u64 = 88u64;\n" - " if (which) { return (s, v); };\n" - " return 5;\n" - "};\n" - "export fn main() i32 = {\n" - " match (mk(true)) {\n" - " case let n: int => { return 1; };\n" - " case let t: (bool, u64) => {\n" - " let (sg, u) = t;\n" - " if (u != 88u64) { return 2; };\n" - " if (!sg) { return 3; };\n" - " return 0;\n" - " };\n" - " };\n" - " return 4;\n" - "};\n", K_RUN, 0, NULL }, - /* C-t0 graduation (was the #242/#243 loud-stop): under the slot-SSoT - * tuple layout narrow elements never share an eightbyte — - * (i32,i32,u64) is 24B (3 slots), the slotted union write fits by - * construction, and the whole tuple round-trips through the union. - * cg_widen_tagged_store's size guard stays as a safety net but can - * no longer fire for an in-cap tuple. */ - { "eightbyte_share", - "package main;\n" - "fn f(a: i32, b: i32, c: u64) ((i32, i32, u64) | void) = {\n" - " return (a, b, c);\n" - "};\n" - "export fn main() i32 = {\n" - " match (f(-3, 4, 9u64)) {\n" - " case let t: (i32, i32, u64) => {\n" - " let (x, y, z) = t;\n" - " if (x != -3) { return 1; };\n" - " if (y != 4) { return 2; };\n" - " if (z != 9u64) { return 3; };\n" - " return 0;\n" - " };\n" - " case void => { return 4; };\n" - " };\n" - " return 5;\n" - "};\n", K_RUN, 0, NULL }, - /* rule-7 loud-stop (ii): a tuple built from a BARE LITERAL element - * (`true`). cstage's cg_tag_for_variant can't type the literal (#241) - * so it returns -1 and loud-stops; wwstage types `true` as bool and - * WOULD resolve the tag, so it mirrors cstage's CONDITION with an - * explicit bare-literal guard (rule 10 — align the richer side DOWN). - * Both stages MUST loud-stop with the SAME diagnostic. Lift both guards - * together when #241 lands cstage literal typing. */ - { "bool_literal", - "package main;\n" - "fn f(x: u64) ((bool, u64) | void) = { return (true, x); };\n" - "export fn main() i32 = { return 0; };\n", - K_BUILDERR, 0, "variant tag unresolved (untyped/literal tuple element" }, -}; - -/* 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/tiu_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tiu_%d_%d.ww", tmpdir, getpid(), i); - snprintf(errf, sizeof errf, "%s/tiu_%d_e_%d", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tiu_%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 (r->kind == K_BUILDERR) { - int ok = (brc != 0) - && (r->experr == NULL || file_contains(errf, r->experr)); - if (!ok) - fprintf(stderr, "row[%s]: %s expected loud #242 builderr " - "(brc=%d)\n", r->label, driver, 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 -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/tiu_bi_%d_%d.ww", getpid(), i); - snprintf(cs_s, sizeof cs_s, "/tmp/tiu_bi_%d_%d_cs.s", getpid(), i); - snprintf(ws_s, sizeof ws_s, "/tmp/tiu_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 " - "(#242 tuple-in-union construct/destructure regression)\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, "tuple_in_union: 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++; - } - } - - /* cs==ww byte-id for the K_RUN rows only; a K_BUILDERR row emits no - * .s on either stage (both loud-stop), so byte-id does not apply. */ - if (access(w6c_ww, X_OK) == 0) { - for (int i = 0; i < n; i++) { - if (rows[i].kind != K_RUN) continue; - total++; - if (byteid(w6c, w6c_ww, &rows[i], i) != 0) fail++; - } - } - - if (fail) { - fprintf(stderr, "tuple_in_union: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("tuple_in_union: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/941_tuple_slot_layout_run.c b/test/wcc/941_tuple_slot_layout_run.c deleted file mode 100644 index 9f525c7b..00000000 --- a/test/wcc/941_tuple_slot_layout_run.c +++ /dev/null @@ -1,2264 +0,0 @@ -/* - * 941_tuple_slot_layout_run — tuple arc C-t0: SLOT layout is the tuple - * SSoT (user-ratified). The checker's TY_TUPLE size counts the stride - * cgen's cursor transport actually writes — a str/slice its 24B header, - * everything else (narrow scalars included) one 8B eightbyte — and the - * t.N read walks stride the same slots. - * - * Pre-C-t0 there were TWO answers: the checker summed PACKED element - * sizes ((u32,u32) = 8B) while every cursor transport site strode 8B - * slots (16B). The 16B shapes were blind to the split (slot == packed); - * the packed shapes hit it everywhere: - * - `let t: (u32,u32) = f()` — cstage's receive keys on the checker - * size (16/32), so sz=8 fell to the generic single-word store - * (word 1 DROPPED) and the packed read walk then read the wrong - * offsets. wwstage (slot-keyed throughout) was runtime-CORRECT - * here — inverted polarity vs the 16B #33 map, gate-blind. - * - a (u32,u32) by-value param: cgfn's receive walk spilled - * 8B/element into a local sized from the PACKED checker size (8B) - * — word 1 landed on the saved-BP slot, frame corruption, SIGSEGV. - * - a mixed (u32,f64)/(u32,str) tuple: packed sz (12/28) missed the - * 16/32 receive arms entirely — silent drop on both stages. - * - * The send-side ABI skew (#32, C-t2) and the wwstage literal-let - * receive (#33, C-t1) are SEPARATE bugs pinned by their own commits; - * this test pins the layout unification only: - * - * row | shape | want - * ---------------------+--------------------------------------+----- - * letcall_packed | let t:(u32,u32)=f(); t.0/t.1 | 0 - * letcall_float_packed | (u32,f64) from call, X0 element | 0 - * letcall_signed_packed| (i32,i32) negatives — MOVSXD reads | 0 - * letcall_mixed_packed | (u32,str): 28B packed missed the 32B | - * | arm; len(t.1) strides a full slot | 0 - * len_elem_packed | len(t.1) of (u32,str) — #235 arm's | - * | slot stride (was BP+4+8) | (in mixed row) - * letcall_16_neutral | (i64,i64) — slot==packed, anchor | 0 - * letcall_32_neutral | (i64,str) — 32B arm, anchor | 0 - * mlet_packed_neutral | let (a,b) = f() packed — per-element | - * | locals, layout-independent anchor | 0 - * - * Every row also asserts cstage/wwstage asm byte-id. - * NNN<950, self-contained (/tmp, no imports) — rule-14's - * selfhost-sibling race does not apply (903/940/945 precedent). - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -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[] = { - { "letcall_packed", - "package main;\n" - "fn f() (u32, u32) = {\n" - " return (3, 4);\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (u32, u32) = f();\n" - " if (t.0 != 3) { return 1; };\n" - " if (t.1 != 4) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "letcall_float_packed", - "package main;\n" - "fn f() (u32, f64) = {\n" - " let a: u32 = 9;\n" - " let x: f64 = 2.5;\n" - " return (a, x);\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (u32, f64) = f();\n" - " if (t.0 != 9) { return 1; };\n" - " if (t.1 != 2.5) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "letcall_signed_packed", - "package main;\n" - "fn f() (i32, i32) = {\n" - " let a: i32 = -5;\n" - " let b: i32 = -6;\n" - " return (a, b);\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (i32, i32) = f();\n" - " if (t.0 != -5) { return 1; };\n" - " if (t.1 != -6) { return 2; };\n" - " if (t.0 + t.1 != -11) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "letcall_mixed_packed", - "package main;\n" - "fn f() (u32, str) = {\n" - " let a: u32 = 7;\n" - " let s: str = \"hello\";\n" - " return (a, s);\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (u32, str) = f();\n" - " if (t.0 != 7) { return 1; };\n" - " if (len(t.1) != 5) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "letcall_16_neutral", - "package main;\n" - "fn f() (i64, i64) = {\n" - " return (41, 17);\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (i64, i64) = f();\n" - " if (t.0 != 41) { return 1; };\n" - " if (t.1 != 17) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "letcall_32_neutral", - "package main;\n" - "fn f() (i64, str) = {\n" - " let a: i64 = 12;\n" - " let s: str = \"wxyz\";\n" - " return (a, s);\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (i64, str) = f();\n" - " if (t.0 != 12) { return 1; };\n" - " if (len(t.1) != 4) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "mlet_packed_neutral", - "package main;\n" - "fn f() (u32, u32) = {\n" - " return (3, 4);\n" - "};\n" - "export fn main() i32 = {\n" - " let (a, b) = f();\n" - " if (a != 3) { return 1; };\n" - " if (b != 4) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - - /* ---- #22 commit 0: the wwstage size()/align() fold (astsize) - * summed PACKED element sizes for N_TTUPLE — a C-t0 escape. The - * type table (tupleelemslot) and cstage both said slot-sum; - * size((u32,u32)) folded to 16 on cstage and 8 on wwstage — - * silent cs≠ww in any folded constant. astsize now reads the - * tuple tinfo, so the slot SSoT has ONE wwstage answer. The - * recursive escapes (tuple inside struct/array sizing) ride the - * same arm. ---- */ - { "c0_sizefold_slot", - "package main;\n" - "export fn main() i32 = {\n" - " if (size((u32, u32)) != 16) { return 1; };\n" - " if (size((size, size)) != 16) { return 2; };\n" - " if (size((u32, str)) != 32) { return 3; };\n" - " if (size((str, str)) != 48) { return 4; };\n" - " if (align((u32, u32)) != 4) { return 5; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "c0_sizefold_recursive", - "package main;\n" - "type holder = struct { t: (u32, u32), x: i64 };\n" - "export fn main() i32 = {\n" - " if (size(holder) != 24) { return 1; };\n" - " if (size([4](u32, u32)) != 64) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - - /* ---- #22a: TAGGED elements in tuples. slot = roundup8(size(elem)) - * — 8B is a FLOOR, not a ceiling (user-ratified 2026-06-04). - * Pre-#22a the checker truncated a tagged element to one 8B slot - * (cstage SIZE 16 vs wwstage astsize 24, cs≠ww) and every cgen - * transport walk strode wide=(STR||SLICE)-else-8 — cstage read the - * NEIGHBOR slot, wwstage read ZEROS (both-wrong-differently, - * byte-cmp-blind). tuple_eslot/tupeslot is now the one stride - * accessor. In-cap shapes work end-to-end; over-cap (>4 GP - * eightbytes — the fold-5b 3-elem shape) stays LOUD until #22b. - * Tagged inits use the CAST form (`5: size`): the bare untyped-int - * widen-store mis-tag is a separate pre-existing bug (task #33). */ - { "t22_sizefold_tagged", - "package main;\n" - "export fn main() i32 = {\n" - " if (size(((void | size), size)) != 24) { return 1; };\n" - " if (size((size, (void | size))) != 24) { return 2; };\n" - " if (size(((void | size), (void | size), size)) != 40) { return 3; };\n" - " if (align(((void | size), size)) != 8) { return 4; };\n" - " if (size((u64, void)) != 8) { return 5; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t22_roundtrip", - "package main;\n" - "fn mk() ((void | size), size) = {\n" - " let mn: (void | size) = 5: size;\n" - " return (mn, 4);\n" - "};\n" - "fn mk2() (size, (void | size)) = {\n" - " let mx: (void | size) = 7: size;\n" - " return (9, mx);\n" - "};\n" - "fn mkv() ((void | size), size) = {\n" - " let mn: (void | size) = void;\n" - " return (mn, 3);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " if (!(t.0 is size)) { return 1; };\n" - " if (t.0 as size != 5) { return 2; };\n" - " if (t.1 != 4) { return 3; };\n" - " let u = mk2();\n" - " if (u.0 != 9) { return 4; };\n" - " if (!(u.1 is size)) { return 5; };\n" - " if (u.1 as size != 7) { return 6; };\n" - " let v = mkv();\n" - " if (!(v.0 is void)) { return 7; };\n" - " if (v.1 != 3) { return 8; };\n" - " let (a, b) = mk();\n" - " if (!(a is size)) { return 9; };\n" - " if (a as size != 5) { return 10; };\n" - " if (b != 4) { return 11; };\n" - " let mn: (void | size) = 6: size;\n" - " let w: ((void | size), size) = (mn, 2);\n" - " if (!(w.0 is size)) { return 12; };\n" - " if (w.0 as size != 6) { return 13; };\n" - " if (w.1 != 2) { return 14; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* mixed SSE/GP with a tagged box: the f64 rides X0 on the SSE - * counter while the tagged element takes 2 consecutive GP - * eightbytes (GP total 3, in-cap) — pins the independent-counter - * interplay the scalar-only rows can't. */ - { "t22_float_mix", - "package main;\n" - "fn mk() (f64, (void | size), i64) = {\n" - " let mn: (void | size) = 6: size;\n" - " return (2.5, mn, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " if (t.0 != 2.5) { return 1; };\n" - " if (!(t.1 is size)) { return 2; };\n" - " if (t.1 as size != 6) { return 3; };\n" - " if (t.2 != 9) { return 4; };\n" - " let (x, y, z) = mk();\n" - " if (x != 2.5) { return 5; };\n" - " if (!(y is size)) { return 6; };\n" - " if (y as size != 6) { return 7; };\n" - " if (z != 9) { return 8; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* TWO tagged elements in-cap (2+2 GP eightbytes == TUPLE_GPCAP): - * narrow u32 payload, negative i64 payload, void variant — - * regression-pins ken's k1 probe (probes evaporate, rows don't). */ - { "t22_two_tagged", - "package main;\n" - "fn mk() ((void | u32), (void | i64)) = {\n" - " let a: (void | u32) = 9: u32;\n" - " let b: (void | i64) = -5: i64;\n" - " return (a, b);\n" - "};\n" - "fn mkv() ((void | u32), (void | i64)) = {\n" - " let a: (void | u32) = void;\n" - " let b: (void | i64) = -41: i64;\n" - " return (a, b);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " if (!(t.0 is u32)) { return 1; };\n" - " if (t.0 as u32 != 9) { return 2; };\n" - " if (!(t.1 is i64)) { return 3; };\n" - " if (t.1 as i64 != -5) { return 4; };\n" - " let v = mkv();\n" - " if (!(v.0 is void)) { return 5; };\n" - " if (v.1 as i64 != -41) { return 6; };\n" - " let (a, b) = mk();\n" - " if (a as u32 != 9) { return 7; };\n" - " if (b as i64 != -5) { return 8; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* tagged FIRST, float second — the SSE/GP counter interplay in the - * other element order (t22_float_mix covers float-first; ken k2 - * proved both orders, this pins the reverse). */ - { "t22_float_mix_rev", - "package main;\n" - "fn mk() ((void | size), f64) = {\n" - " let mn: (void | size) = 6: size;\n" - " return (mn, 0.25);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " if (!(t.0 is size)) { return 1; };\n" - " if (t.0 as size != 6) { return 2; };\n" - " if (t.1 != 0.25) { return 3; };\n" - " let (y, z) = mk();\n" - " if (y as size != 6) { return 4; };\n" - " if (z != 0.25) { return 5; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* tagged tuple element as a CALL ARG — the call-boundary SEND of - * the ruled condition-(c) matrix. cstage rides its generic - * node_istaggedarg cursor push; wwstage's kind-gated aistagged - * missed N_DOT and mis-routed t.0 into the widening branch - * (clamped tag 0 — callee read `void`, silent). reviewer-22 fix: - * cgenutil.ww N_DOT arm (aistagged + pushargsrev). */ - { "t22_elem_arg", - "package main;\n" - "fn takes(v: (void | size)) size = {\n" - " if (v is size) { return v as size; };\n" - " return 0;\n" - "};\n" - "export fn main() i32 = {\n" - " let mn: (void | size) = 5: size;\n" - " let t: ((void | size), size) = (mn, 4);\n" - " if (takes(t.0) != 5) { return 1; };\n" - " if (takes(t.0) + t.1 != 9) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - - /* rule-7 louds: every tagged-tuple route that is not - * correct-via-accessor must die LOUD (the #22a exit invariant). */ - { "t22_reject_arg", - "package main;\n" - "fn send(t: ((void | size), size)) size = { return t.1; };\n" - "export fn main() i32 = {\n" - " let mn: (void | size) = 5: size;\n" - " let t: ((void | size), size) = (mn, 4);\n" - " return send(t): i32;\n" - "};\n", 0, - K_BUILDERR, "tuple arg element kind unsupported" }, - /* ---- #22b (task #28): over-cap (sret, MEMORY-class) tagged-tuple - * transport. Every K_RUN row here was a BUILD-FAIL at 22a's exit - * bound ("#22b: tagged element in an over-cap (sret) tuple return - * unwired") — the mutation story is build-fail→runtime, not - * wrong-value→right-value. The driver is regex fold-5b's - * parse_repetition: ((void|size),(void|size),size) = 5 GP - * eightbytes > TUPLE_GPCAP=4. ---- */ - { "b22_parse_repetition", - "package main;\n" - "fn pr() ((void | size), (void | size), size) = {\n" - " let mn: (void | size) = void;\n" - " let mx: (void | size) = 5: size;\n" - " mn = 3: size;\n" - " return (mn, mx, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = pr();\n" - " if (!(t.0 is size)) { return 1; };\n" - " if (t.0 as size != 3) { return 2; };\n" - " if (!(t.1 is size)) { return 3; };\n" - " if (t.1 as size != 5) { return 4; };\n" - " if (t.2 != 9) { return 5; };\n" - " let (a, b, n) = pr();\n" - " if (a as size != 3) { return 6; };\n" - " if (b as size != 5) { return 7; };\n" - " if (n != 9) { return 8; };\n" - " let a2: (void | size) = void;\n" - " let b2: (void | size) = void;\n" - " let n2: size = 0;\n" - " a2, b2, n2 = pr();\n" - " if (a2 as size != 3) { return 9; };\n" - " if (b2 as size != 5) { return 10; };\n" - " if (n2 != 9) { return 11; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* tagged at HEAD / MID / TAIL of over-cap tuples + void variants + - * destructure-from-IDENT of a received over-cap tuple (the memory- - * source MLET arm, no register cursor). */ - { "b22_head_mid_tail", - "package main;\n" - "fn ph() ((void | size), size, size, size, size) = {\n" - " let a: (void | size) = 11: size;\n" - " return (a, 1, 2, 3, 4);\n" - "};\n" - "fn pm() (size, size, (void | size), size, size) = {\n" - " let a: (void | size) = 12: size;\n" - " return (1, 2, a, 3, 4);\n" - "};\n" - "fn pt() (size, size, size, size, (void | size)) = {\n" - " let a: (void | size) = 13: size;\n" - " return (1, 2, 3, 4, a);\n" - "};\n" - "fn pv() ((void | size), (void | size), size) = {\n" - " let a: (void | size) = void;\n" - " let b: (void | size) = void;\n" - " return (a, b, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let h = ph();\n" - " if (h.0 as size != 11) { return 1; };\n" - " if (h.4 != 4) { return 2; };\n" - " let m = pm();\n" - " if (m.0 != 1) { return 3; };\n" - " if (m.2 as size != 12) { return 4; };\n" - " if (m.4 != 4) { return 5; };\n" - " let t = pt();\n" - " if (t.3 != 4) { return 6; };\n" - " if (t.4 as size != 13) { return 7; };\n" - " let v = pv();\n" - " if (!(v.0 is void)) { return 8; };\n" - " if (!(v.1 is void)) { return 9; };\n" - " if (v.2 != 9) { return 10; };\n" - " let (x, y, z) = v;\n" - " if (!(x is void)) { return 11; };\n" - " if (!(y is void)) { return 12; };\n" - " if (z != 9) { return 13; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* str + tagged mixed over-cap (3+2+1+1 = 7 GP words): the wide - * AX/BX/CX header store and the tagged mem-to-mem box copy - * interleave through the same sret pointer. */ - { "b22_str_tagged_mix", - "package main;\n" - "fn pr() (str, (void | size), size, size) = {\n" - " let mid: (void | size) = 4: size;\n" - " return (\"hello\", mid, 6, 7);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = pr();\n" - " if (t.0.len != 5) { return 1; };\n" - " if (t.1 as size != 4) { return 2; };\n" - " if (t.2 != 6) { return 3; };\n" - " if (t.3 != 7) { return 4; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* SSE-over-cap (3 floats > TUPLE_SSECAP) with a tagged element: - * the X0 float store and the tagged box copy ride independent - * walks of the same slot-laid buffer. */ - { "b22_sse_mix", - "package main;\n" - "fn pr() ((void | size), f64, f64, f64) = {\n" - " let a: (void | size) = 6: size;\n" - " return (a, 1.5, 2.5, 3.5);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = pr();\n" - " if (t.0 as size != 6) { return 1; };\n" - " if (t.1 != 1.5) { return 2; };\n" - " if (t.2 != 2.5) { return 3; };\n" - " if (t.3 != 3.5) { return 4; };\n" - " let (a, x, y, z) = pr();\n" - " if (a as size != 6) { return 5; };\n" - " if (x != 1.5) { return 6; };\n" - " if (y != 2.5) { return 7; };\n" - " if (z != 3.5) { return 8; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* harec `_` skip in over-cap MASSIGN + MLET: the skipped element - * still advances foff by its slot (a tagged 16B box, not 8). */ - { "b22_underscore_skip", - "package main;\n" - "fn pr() ((void | size), (void | size), size) = {\n" - " let mn: (void | size) = 3: size;\n" - " let mx: (void | size) = 5: size;\n" - " return (mn, mx, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let a: (void | size) = void;\n" - " let n: size = 0;\n" - " a, _, n = pr();\n" - " if (a as size != 3) { return 1; };\n" - " if (n != 9) { return 2; };\n" - " let (q, _, r) = pr();\n" - " if (q as size != 3) { return 3; };\n" - " if (r != 9) { return 4; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* residual loud (rule 7): tagged element from a NON-IDENT source - * in an over-cap return — the cursor can't carry the box past the - * DX dest-base reload; #23/#40 follow-ups. */ - { "b22_reject_nonident_source", - "package main;\n" - "fn g() (void | size) = { return 5: size; };\n" - "fn pr() ((void | size), (void | size), size) = {\n" - " let mx: (void | size) = 2: size;\n" - " return (g(), mx, 7);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = pr();\n" - " return t.2: i32;\n" - "};\n", 0, - K_BUILDERR, "#22b: tagged element in an over-cap (sret) tuple " - "return from a non-ident or widening source unwired" }, - /* classify/emit agreement: the declared type srets (5 eightbytes) - * but the UNWIDENED payload literal counted 4 expr-shape words — - * pre-#22b the emit took the register path against an sret - * caller: built clean, ran garbage, BOTH stages, byte-id - * (gate-blind). The decision now rides the declared classifier; - * the widening store itself is the #23/#40 follow-up. */ - { "b22_reject_widen_skew", - "package main;\n" - "fn pr() ((void | size), (void | size), size) = {\n" - " let mx: (void | size) = 5: size;\n" - " return (1: size, mx, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = pr();\n" - " return t.2: i32;\n" - "};\n", 0, - K_BUILDERR, "#22b: tagged element in an over-cap (sret) tuple " - "return from a non-ident or widening source unwired" }, - /* #37 FLIP (was the #22b loud bound; BUILDERR at master a72e815 - * with "tagged tuple element read exceeds the AX/DX/CX/R8 box - * cursor"): a >32B box t.N READ now leaves the box ADDRESS in AX - * (cg_tagged_memread, the sret-receive convention) and every - * cursor consumer copies from memory. The pre-#22b history: cstage - * emitted INVALID ASM (tuple_rseq[4] OOB), wwstage clamped k>=3 to - * R8 and silently dropped payload word 3. Readback covers the - * is-test (N_TYPETEST mem tag load) AND the full payload through a - * direct match on t.N (match-spill mem copy). */ - { "c37_big_box_t0_read", - "package main;\n" - "type four = struct { a: u64, b: u64, c: u64, d: u64, };\n" - "fn g() (void | four) = {\n" - " let v: four = four{a=1u64, b=2u64, c=3u64, d=4u64};\n" - " return v;\n" - "};\n" - "fn pr() ((void | four), size) = {\n" - " let e: (void | four) = g();\n" - " return (e, 7);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = pr();\n" - " if (t.1 != 7) { return 1; };\n" - " if (!(t.0 is four)) { return 2; };\n" - " match (t.0) {\n" - " case let f: four => {\n" - " if (f.a != 1) { return 3; };\n" - " if (f.b != 2) { return 4; };\n" - " if (f.c != 3) { return 5; };\n" - " if (f.d != 4) { return 6; };\n" - " };\n" - " case void => { return 7; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* #37 THE 5b DRIVER SHAPE: `match insts[pc]` where inst = - * (inst_lit | inst_repeat) = 56B (8 tag + 48 payload: 2x size + - * 2x 16B nested tagged). SILENT-WRONG at master a72e815 (gate- - * blind, byte-id both stages): the N_INDEX cursor load clamped at - * R8, dropping payload words 3+ — min's payload and max's whole - * box read stack garbage. Matches BOTH variants, reads every - * nested-tagged field back. */ - { "c37_idx_match_56", - "package main;\n" - "type inst_lit = rune;\n" - "type inst_repeat = struct {\n" - " id: size,\n" - " origin: size,\n" - " min: (void | size),\n" - " max: (void | size),\n" - "};\n" - "type inst = (inst_lit | inst_repeat);\n" - "export fn main() i32 = {\n" - " let insts: []inst = [];\n" - " let r = inst_repeat{id=7: size, origin=3: size, min=(11: size), max=(22: size)};\n" - " append(insts, r);\n" - " append(insts, ('A': inst_lit));\n" - " let pc: size = 0;\n" - " match (insts[pc]) {\n" - " case let ir: inst_repeat => {\n" - " if (ir.id != 7) { return 1; };\n" - " if (ir.origin != 3) { return 2; };\n" - " if (!(ir.min is size)) { return 3; };\n" - " if (ir.min as size != 11) { return 4; };\n" - " if (!(ir.max is size)) { return 5; };\n" - " if (ir.max as size != 22) { return 6; };\n" - " };\n" - " case let l: inst_lit => { return 7; };\n" - " };\n" - " match (insts[1]) {\n" - " case let l: inst_lit => {\n" - " if (l != 'A') { return 8; };\n" - " };\n" - " case let ir: inst_repeat => { return 9; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* #37: str + nested-tagged payload variant — the big variant - * carries a 24B str header AND a nested 16B box (64B union); - * readback checks the str CONTENT survives the mem copy, not just - * the tag. SILENT-WRONG at master (words 3+ dropped). */ - { "c37_idx_match_str_nested", - "package main;\n" - "type lit2 = rune;\n" - "type rep2 = struct {\n" - " name: str,\n" - " min: (void | size),\n" - " max: (void | size),\n" - "};\n" - "type in2 = (lit2 | rep2);\n" - "export fn main() i32 = {\n" - " let xs: []in2 = [];\n" - " let r = rep2{name=\"deadbeef\", min=(2: size), max=(5: size)};\n" - " append(xs, r);\n" - " match (xs[0]) {\n" - " case let v: rep2 => {\n" - " if (v.name.len != 8) { return 1; };\n" - " if (v.name[0] != 'd') { return 2; };\n" - " if (v.name[7] != 'f') { return 3; };\n" - " if (!(v.min is size)) { return 4; };\n" - " if (v.min as size != 2) { return 5; };\n" - " if (v.max as size != 5) { return 6; };\n" - " };\n" - " case let l: lit2 => { return 7; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* #37: let-bind from a >32B indexed read (widener subset arm - * mem-copy), then is-test directly on the indexed read (N_TYPETEST - * mem tag load), then reassign (cgassign route). SILENT-WRONG at - * master. */ - { "c37_idx_let_is_assign", - "package main;\n" - "type inst_lit = rune;\n" - "type inst_repeat = struct {\n" - " id: size,\n" - " origin: size,\n" - " min: (void | size),\n" - " max: (void | size),\n" - "};\n" - "type inst = (inst_lit | inst_repeat);\n" - "export fn main() i32 = {\n" - " let insts: []inst = [];\n" - " let r = inst_repeat{id=7: size, origin=3: size, min=(11: size), max=(22: size)};\n" - " append(insts, r);\n" - " append(insts, ('A': inst_lit));\n" - " if (!(insts[0] is inst_repeat)) { return 1; };\n" - " if (insts[0] is inst_lit) { return 2; };\n" - " let w = insts[0];\n" - " match (w) {\n" - " case let ir: inst_repeat => {\n" - " if (ir.max as size != 22) { return 3; };\n" - " };\n" - " case let l: inst_lit => { return 4; };\n" - " };\n" - " w = insts[1];\n" - " if (!(w is inst_lit)) { return 5; };\n" - " w = insts[0];\n" - " match (w) {\n" - " case let ir: inst_repeat => {\n" - " if (ir.min as size != 11) { return 6; };\n" - " };\n" - " case let l: inst_lit => { return 7; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* #37: exact-type >32B tagged RETURN from an indexed source — was - * the "#38b: >32B tagged return from a cursor source" LOUD bound; - * now routes through the widener's mem-read arm into the sret - * dest. BUILDERR at master a72e815. */ - { "c37_idx_return", - "package main;\n" - "type inst_lit = rune;\n" - "type inst_repeat = struct {\n" - " id: size,\n" - " origin: size,\n" - " min: (void | size),\n" - " max: (void | size),\n" - "};\n" - "type inst = (inst_lit | inst_repeat);\n" - "fn pick(insts: []inst, pc: size) inst = {\n" - " return insts[pc];\n" - "};\n" - "export fn main() i32 = {\n" - " let insts: []inst = [];\n" - " let r = inst_repeat{id=7: size, origin=3: size, min=(11: size), max=(22: size)};\n" - " append(insts, r);\n" - " let w = pick(insts, 0);\n" - " match (w) {\n" - " case let ir: inst_repeat => {\n" - " if (ir.id != 7) { return 1; };\n" - " if (ir.min as size != 11) { return 2; };\n" - " };\n" - " case let l: inst_lit => { return 3; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* #37: WIDENING let from a >32B indexed read — subset mem-copy + - * zero-pad + (identity) tag remap; and the 56B (>48B MEMORY-class) - * exact-type ARG from an indexed source (the pre-existing #38b - * memarg stack-blit path, kept working alongside the new emitters: - * cgplaceaddr never sees the cursor). */ - { "c37_widen_and_memarg", - "package main;\n" - "type inst_lit = rune;\n" - "type inst_repeat = struct {\n" - " id: size,\n" - " origin: size,\n" - " min: (void | size),\n" - " max: (void | size),\n" - "};\n" - "type inst = (inst_lit | inst_repeat);\n" - "type wide = (inst_lit | inst_repeat | str);\n" - "fn idof(i: inst) size = {\n" - " match (i) {\n" - " case let ir: inst_repeat => { return ir.id; };\n" - " case let l: inst_lit => { return 999; };\n" - " };\n" - " return 998;\n" - "};\n" - "export fn main() i32 = {\n" - " let insts: []inst = [];\n" - " let r = inst_repeat{id=7: size, origin=3: size, min=(11: size), max=(22: size)};\n" - " append(insts, r);\n" - " let w: wide = insts[0];\n" - " match (w) {\n" - " case let ir: inst_repeat => {\n" - " if (ir.max as size != 22) { return 1; };\n" - " };\n" - " case let l: inst_lit => { return 2; };\n" - " case let s2: str => { return 3; };\n" - " };\n" - " if (idof(insts[0]) != 7) { return 4; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* #37: NON-identity tag remap on a >32B mem-read widen — source - * union declares its variants in REVERSED order vs dst, so the - * widener's remap chain must fire (tag 0 -> 1) after the mem copy - * + zero-pad. SILENT-WRONG at master a72e815. */ - { "c37_widen_remap", - "package main;\n" - "type inst_lit = rune;\n" - "type inst_repeat = struct {\n" - " id: size,\n" - " origin: size,\n" - " min: (void | size),\n" - " max: (void | size),\n" - "};\n" - "type rev = (inst_repeat | inst_lit);\n" - "type wide = (inst_lit | inst_repeat | str);\n" - "export fn main() i32 = {\n" - " let xs: []rev = [];\n" - " let r = inst_repeat{id=7: size, origin=3: size, min=(11: size), max=(22: size)};\n" - " append(xs, r);\n" - " let w: wide = xs[0];\n" - " match (w) {\n" - " case let ir: inst_repeat => {\n" - " if (ir.id != 7) { return 1; };\n" - " if (!(ir.max is size)) { return 2; };\n" - " if (ir.max as size != 22) { return 3; };\n" - " };\n" - " case let l: inst_lit => { return 4; };\n" - " case let s2: str => { return 5; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* ken X1 (/tmp/ken_22b_validation.md, impl-22b flag: ROW-WORTHY): - * the #22a/#23/#22b/#37 composition — a #23-constructed box - * ((void|st), st from an inline struct-lit with a NESTED TAGGED - * FIELD, box exactly 32B = cursor AT cap) transported through the - * #22b over-cap sret return, every field read back. */ - { "c37_x1_compose", - "package main;\n" - "type st = struct { a: size, m: (void | size), };\n" - "fn pr() ((void | st), size, size) = {\n" - " let e: (void | st) = st{a=4: size, m=(2: size)};\n" - " return (e, 5, 6);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = pr();\n" - " if (!(t.0 is st)) { return 1; };\n" - " match (t.0) {\n" - " case let v: st => {\n" - " if (v.a != 4) { return 2; };\n" - " if (!(v.m is size)) { return 3; };\n" - " if (v.m as size != 2) { return 4; };\n" - " };\n" - " case void => { return 5; };\n" - " };\n" - " if (t.1 != 5) { return 6; };\n" - " if (t.2 != 6) { return 7; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* #37 boundary neighbor: a 32B-EXACT (cursor AT cap) tagged - * element INDEX read keeps the AX/DX/CX/R8 cursor — asm byte- - * identical to master (probed: p6 asm unchanged vs a72e815). - * Sister of b22_k5_box_at_cap (the t.N boundary). */ - { "c37_idx_at_cap", - "package main;\n" - "type box = (void | str);\n" - "export fn main() i32 = {\n" - " let bs: []box = [];\n" - " let s: str = \"abcdefgh\";\n" - " append(bs, s);\n" - " match (bs[0]) {\n" - " case let v: str => {\n" - " if (v.len != 8) { return 1; };\n" - " };\n" - " case void => { return 2; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* #37 ptr-chained route: match on p.f through a *struct base — - * the cstage D_BX LEAQ arm / wwstage cgloadtaggedfield gate. The - * only #37 emitter pair otherwise covered by probes alone (ken - * M4); unlike the local-field shape (#42 cs!=ww divergence, - * disclosed), the ptr-base spelling is byte-id. SILENT-WRONG at - * master f272068 (exit 1, truncated payload). */ - { "c37_ptr_field_match", - "package main;\n" - "type inst_lit = rune;\n" - "type inst_repeat = struct {\n" - " id: size,\n" - " origin: size,\n" - " min: (void | size),\n" - " max: (void | size),\n" - "};\n" - "type inst = (inst_lit | inst_repeat);\n" - "type holder = struct { pre: size, f: inst, };\n" - "export fn main() i32 = {\n" - " let g = holder{pre=9: size, f=inst_repeat{id=7: size, origin=3: size, min=(11: size), max=(55: size)}};\n" - " let p = &g;\n" - " if (!(p.f is inst_repeat)) { return 1; };\n" - " match (p.f) {\n" - " case let ir: inst_repeat => {\n" - " if (ir.id != 7) { return 2; };\n" - " if (ir.max as size != 55) { return 3; };\n" - " };\n" - " case let l: inst_lit => { return 4; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* #37 non-ident INDEX base: (*p)[i] rides the cgindex fallback - * arm (AX already the element address — the emitter that LEAVES - * AX rather than LEAQing). SILENT-WRONG at master f272068. */ - { "c37_deref_idx_match", - "package main;\n" - "type inst_lit = rune;\n" - "type inst_repeat = struct {\n" - " id: size,\n" - " origin: size,\n" - " min: (void | size),\n" - " max: (void | size),\n" - "};\n" - "type inst = (inst_lit | inst_repeat);\n" - "export fn main() i32 = {\n" - " let insts: []inst = [];\n" - " let r = inst_repeat{id=7: size, origin=3: size, min=(11: size), max=(22: size)};\n" - " append(insts, r);\n" - " let p = &insts;\n" - " match ((*p)[0]) {\n" - " case let ir: inst_repeat => {\n" - " if (ir.id != 7) { return 1; };\n" - " if (ir.max as size != 22) { return 2; };\n" - " };\n" - " case let l: inst_lit => { return 3; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* #37→#35 FLIP (Family C): the >32B deref let-source loud is now - * WIRED — taggedmemread covers N_UN(STAR) at any size, so the - * widener's mem-read arm copies the box. The loud-symmetry pin - * (reviewer-37) becomes a runtime row. */ - { "fc_deref_56_slice_let", - "package main;\n" - "type inst_lit = rune;\n" - "type inst_repeat = struct {\n" - " id: size,\n" - " origin: size,\n" - " min: (void | size),\n" - " max: (void | size),\n" - "};\n" - "type inst = (inst_lit | inst_repeat);\n" - "export fn main() i32 = {\n" - " let insts: []inst = [];\n" - " let r = inst_repeat{id=7: size, origin=3: size, min=(11: size), max=(22: size)};\n" - " append(insts, r);\n" - " let p = &insts[0];\n" - " let w = *p;\n" - " if (!(w is inst_repeat)) { return 1; };\n" - " match (w) {\n" - " case let ir: inst_repeat => {\n" - " if (ir.id != 7 || ir.origin != 3) { return 2; };\n" - " if ((ir.min as size) != 11) { return 3; };\n" - " if ((ir.max as size) != 22) { return 4; };\n" - " };\n" - " case let l: inst_lit => { return 5; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* #37→#35 FLIP (Family C): match on a >32B deref scrutinee is - * now wired through the mem-read spill (matchscrutt carries the - * N_UN stamped type, sizing @match_spill off the box). */ - { "fc_deref_56_slice_match", - "package main;\n" - "type inst_lit = rune;\n" - "type inst_repeat = struct {\n" - " id: size,\n" - " origin: size,\n" - " min: (void | size),\n" - " max: (void | size),\n" - "};\n" - "type inst = (inst_lit | inst_repeat);\n" - "export fn main() i32 = {\n" - " let insts: []inst = [];\n" - " let r = inst_repeat{id=7: size, origin=3: size, min=(11: size), max=(22: size)};\n" - " append(insts, r);\n" - " let p = &insts[0];\n" - " match (*p) {\n" - " case let ir: inst_repeat => {\n" - " if (ir.id != 7) { return 1; };\n" - " if (!(ir.min is size)) { return 2; };\n" - " if ((ir.max as size) != 22) { return 3; };\n" - " };\n" - " case let l: inst_lit => { return 4; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* ...but the >32B TRANSPORT itself (sret send mem-to-mem + MLET - * copy-out) is size-generic and correct — only the cursor read is - * bounded. is-checks on the destructured local read the tag from - * its own slot. */ - { "b22_big_box_mlet", - "package main;\n" - "type four = struct { a: u64, b: u64, c: u64, d: u64, };\n" - "fn g() (void | four) = {\n" - " let v: four = four{a=1u64, b=2u64, c=3u64, d=4u64};\n" - " return v;\n" - "};\n" - "fn pr() ((void | four), size) = {\n" - " let e: (void | four) = g();\n" - " return (e, 7);\n" - "};\n" - "export fn main() i32 = {\n" - " let e2 = g();\n" - " if (!(e2 is four)) { return 1; };\n" - " let (e, n) = pr();\n" - " if (n != 7) { return 3; };\n" - " if (!(e is four)) { return 4; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* ken K4 (/tmp/ken_22b_validation.md): declared-VOID-elem over-cap - * return — NOT checker-unreachable; at base 6699158 it BUILT and - * ran silently WRONG (the send's old wide?esz:8 advanced 8 for the - * void elem while every receive walks its 0-slot — misaligned by - * one word). The tuple_eslot 0-advance fixes a LIVE base silent - * miscompile, not just a latent skew. */ - { "b22_void_elem_k4", - "package main;\n" - "fn mk() (void, size, size, size, size, size) = {\n" - " return (void, 1, 2, 3, 4, 5);\n" - "};\n" - "export fn main() i32 = {\n" - " let (v, a, b, c, d, e) = mk();\n" - " if (a != 1) { return 1; };\n" - " if (b != 2) { return 2; };\n" - " if (c != 3) { return 3; };\n" - " if (d != 4) { return 4; };\n" - " if (e != 5) { return 5; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* ken K5 (/tmp/ken_22b_validation.md delta re-OK, reviewer-22b - * amendment): a 32B-EXACT box — (void|str) = tag + 24B header = 4 - * words — puts the t.N read cursor exactly AT the AX/DX/CX/R8 cap. - * Pins the k < eslot/8 bound from below (no off-by-one: the str - * header's third word rides R8); base louds the #22b unwired text. */ - { "b22_k5_box_at_cap", - "package main;\n" - "fn mk() ((void | str), size, size) = {\n" - " let m: (void | str) = \"boundary\";\n" - " return (m, 5, 6);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " if (!(t.0 is str)) { return 1; };\n" - " match (t.0) {\n" - " case let s: str => {\n" - " if (s.len != 8) { return 2; };\n" - " };\n" - " case void => { return 9; };\n" - " };\n" - " if (t.1 != 5) { return 3; };\n" - " if (t.2 != 6) { return 4; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* reviewer-22b gap probe: tagged in BOTH positions of a 2-elem - * over-cap tuple — a 32B box + a 16B box (6 GP words), no scalar - * spacer between the two mem-to-mem copies. Base louds. */ - { "b22_both_tagged_2elem", - "package main;\n" - "fn pr() ((void | str), (void | size)) = {\n" - " let a: (void | str) = \"boundary\";\n" - " let b: (void | size) = 7: size;\n" - " return (a, b);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = pr();\n" - " if (!(t.0 is str)) { return 1; };\n" - " match (t.0) {\n" - " case let s: str => { if (s.len != 8) { return 2; }; };\n" - " case void => { return 9; };\n" - " };\n" - " if (!(t.1 is size)) { return 3; };\n" - " if (t.1 as size != 7) { return 4; };\n" - " let (x, y) = pr();\n" - " if (!(x is str)) { return 5; };\n" - " if (y as size != 7) { return 6; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* reviewer-22b gap probe: CHAINED sret — a fn that receives an - * over-cap tagged tuple via sret (destructure to ident locals) and - * returns one through its own sret arg; the received locals are - * exactly the ident-only shapes the send arm admits. (Direct - * `return t` forwarding is the #39 acceptance divergence.) */ - { "b22_chained_sret", - "package main;\n" - "fn g() ((void | size), (void | size), size) = {\n" - " let mn: (void | size) = 3: size;\n" - " let mx: (void | size) = 5: size;\n" - " return (mn, mx, 9);\n" - "};\n" - "fn h() ((void | size), (void | size), size) = {\n" - " let (a, b, n) = g();\n" - " return (a, b, n + 1);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = h();\n" - " if (t.0 as size != 3) { return 1; };\n" - " if (t.1 as size != 5) { return 2; };\n" - " if (t.2 != 10) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* the let-twin of the skew: a VOID-bearing over-cap literal init - * slipped wwstage's expr-count loud in-cap and fell past every - * store arm to NOTHING (cs louded via the ken-R1 net, ww ran - * silent-wrong — acceptance divergence). wwstage now carries the - * same net. */ - { "b22_reject_let_void_slip", - "package main;\n" - "fn pr() ((void | size), (void | size), size) = {\n" - " let t: ((void | size), (void | size), size) = (void, void, 1);\n" - " return t;\n" - "};\n" - "export fn main() i32 = {\n" - " let t = pr();\n" - " return t.2: i32;\n" - "};\n", 0, - K_BUILDERR, "over-cap tuple initialiser from a non-call source " - "unwired" }, - /* ken R1: an OVER-CAP tuple literal init (a tagged box >16B pushes - * the shape past TUPLE_GPCAP) fell past every cstage N_LET store - * arm to NOTHING — silent uninitialized-frame reads — while - * wwstage loud-rejected. Pre-existing for (str,str) literals; the - * #22a tagged slots routed tagged shapes into it. Both pins. */ - { "t22_reject_overcap_lit_tagged", - "package main;\n" - "export fn main() i32 = {\n" - " let e: (void | u32 | str) = \"abc\";\n" - " let t: (u64, (void | u32 | str)) = (7u64, e);\n" - " if (t.0 != 7u64) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "tuple literal exceeds register-return ABI capacity" }, - { "t22_reject_overcap_lit_strs", - "package main;\n" - "export fn main() i32 = {\n" - " let t: (str, str) = (\"ab\", \"cde\");\n" - " if (t.0.len != 2) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "tuple literal exceeds register-return ABI capacity" }, - { "t22_reject_lit_call_elem", - "package main;\n" - "fn g() (void | size) = { return 5: size; };\n" - "fn mk() ((void | size), size) = { return (g(), 4); };\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " return t.1: i32;\n" - "};\n", 0, - K_BUILDERR, "tagged tuple element from a non-local source shape " - "unwired" }, - { "t22_reject_global_init", - "package main;\n" - "let g: ((void | size), i64) = (5, 4);\n" - "export fn main() i32 = { return g.1: i32; };\n", 0, - K_BUILDERR, "unsupported element init (int/str literals only; " - "rule 7)" }, - /* pre-existing loud (no-regress pin): tuple element WRITE. */ - { "t22_reject_elem_write", - "package main;\n" - "export fn main() i32 = {\n" - " let mn: (void | size) = 5: size;\n" - " let t: ((void | size), size) = (mn, 4);\n" - " t.1 = 9;\n" - " return t.1: i32;\n" - "};\n", 0, - K_BUILDERR, "unsupported assign target shape" }, - - /* ---- over-cap (sret) NARROW-scalar stride: the sret buffer is - * slot-laid (C-t0) on EVERY side — send, let-receive, MLET, and - * MASSIGN copy-out. Pre-review the wwstage SEND and MASSIGN - * receive strode packed esz (4 for u32): self-consistent at base - * (ran right, byte-diff), but #22a's MLET-receive slot flip alone - * made ww read c at 32 while its send wrote 28 — a ww runtime - * REGRESSION the suite had no row for (reviewer-22). (str,u32,str) - * = 7 GP words, over-cap. ---- */ - { "sret_narrow_mix_let", - "package main;\n" - "fn f() (str, u32, str) = {\n" - " let a: str = \"abc\";\n" - " let b: u32 = 7;\n" - " let c: str = \"wxyz\";\n" - " return (a, b, c);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = f();\n" - " if (len(t.0) != 3) { return 1; };\n" - " if (t.1 != 7) { return 2; };\n" - " if (len(t.2) != 4) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "sret_narrow_mix_mlet", - "package main;\n" - "fn f() (str, u32, str) = {\n" - " let a: str = \"abc\";\n" - " let b: u32 = 7;\n" - " let c: str = \"wxyz\";\n" - " return (a, b, c);\n" - "};\n" - "export fn main() i32 = {\n" - " let (a, b, c) = f();\n" - " if (len(a) != 3) { return 1; };\n" - " if (b != 7) { return 2; };\n" - " if (len(c) != 4) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "sret_narrow_mix_massign", - "package main;\n" - "fn f() (str, u32, str) = {\n" - " let a: str = \"abc\";\n" - " let b: u32 = 7;\n" - " let c: str = \"wxyz\";\n" - " return (a, b, c);\n" - "};\n" - "export fn main() i32 = {\n" - " let a: str = \"\";\n" - " let b: u32 = 0;\n" - " let c: str = \"\";\n" - " a, b, c = f();\n" - " if (len(a) != 3) { return 1; };\n" - " if (b != 7) { return 2; };\n" - " if (len(c) != 4) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - - /* ---- C-t1 (#33): the let RECEIVE re-keyed onto the declared - * type's register classify. Pre-C-t1 wwstage keyed on producer - * SHAPE (mixed-str syntactic / rettupleof N_CALL) so a - * scalar-scalar LITERAL matched neither and dropped word 1; - * cstage keyed on sz==16/32 so 24B 3-scalar shapes dropped words - * on BOTH sources. ---- */ - { "t1_lit_packed", - "package main;\n" - "fn second() u32 = {\n" - " let t: (u32, u32) = (3, 4);\n" - " if (t.0 != 3) { return 99; };\n" - " return t.1;\n" - "};\n" - "export fn main() i32 = {\n" - " if (second() != 4) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t1_lit_16", - "package main;\n" - "export fn main() i32 = {\n" - " let t: (i64, i64) = (3, 4);\n" - " if (t.0 != 3) { return 1; };\n" - " if (t.1 != 4) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t1_lit_mixed", - "package main;\n" - "export fn main() i32 = {\n" - " let t: (i64, str) = (12, \"wxyz\");\n" - " if (t.0 != 12) { return 1; };\n" - " if (len(t.1) != 4) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t1_lit_3scalar", - "package main;\n" - "fn second() i64 = {\n" - " let t: (i64, i64, i64) = (3, 4, 5);\n" - " if (t.0 != 3) { return -1; };\n" - " if (t.1 != 4) { return -2; };\n" - " return t.2;\n" - "};\n" - "export fn main() i32 = {\n" - " if (second() != 5) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t1_call_3scalar", - "package main;\n" - "fn f() (i64, i64, i64) = {\n" - " return (7, 8, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (i64, i64, i64) = f();\n" - " if (t.0 != 7) { return 1; };\n" - " if (t.1 != 8) { return 2; };\n" - " if (t.2 != 9) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t1_call_noannot_neutral", - "package main;\n" - "fn f() (i64, i64) = {\n" - " return (3, 4);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = f();\n" - " if (t.0 != 3) { return 1; };\n" - " if (t.1 != 4) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - - /* ---- C-t2 (#32): the by-value tuple ARG send. node_tuplearg - * was N_CALL-scoped (and its comment FALSELY claimed the rest - * loud-stop): a tuple ident/literal/unwrap arg fell to the - * scalar single-PUSHQ default, skewing every later arg register - * — the callee read garbage word 2 (packed shapes SIGSEGV'd - * pre-C-t0). Now every cursor-filling producer rides the #163 - * @tupargscr restage; any other tuple-typed source loud-stops. ---- */ - { "t2_param_packed", - "package main;\n" - "fn pick(t: (u32, u32), k: i32) u32 = {\n" - " if (k == 0) { return t.0; };\n" - " return t.1;\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (u32, u32) = (41, 17);\n" - " if (pick(t, 0) != 41) { return 1; };\n" - " if (pick(t, 1) != 17) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t2_param_16", - "package main;\n" - "fn pick(t: (i64, i64), k: i32) i64 = {\n" - " if (k == 0) { return t.0; };\n" - " return t.1;\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (i64, i64) = (41, 17);\n" - " if (pick(t, 0) != 41) { return 1; };\n" - " if (pick(t, 1) != 17) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t2_mixed_order", - "package main;\n" - "fn g(a: i64, t: (u32, u32), b: i64) i64 = {\n" - " return a*1000 + (t.0: i64)*100 + (t.1: i64)*10 + b;\n" - "};\n" - "fn h(t: (i64, i64), s: str, k: i64) i64 = {\n" - " return t.0 + t.1 + (len(s): i64) + k;\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (u32, u32) = (3, 4);\n" - " if (g(1, t, 2) != 1342) { return 1; };\n" - " let u: (i64, i64) = (10, 20);\n" - " if (h(u, \"abc\", 7) != 40) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t2_lit_arg", - "package main;\n" - "fn pick(t: (i64, i64), k: i32) i64 = {\n" - " if (k == 0) { return t.0; };\n" - " return t.1;\n" - "};\n" - "export fn main() i32 = {\n" - " if (pick((41, 17), 0) != 41) { return 1; };\n" - " if (pick((41, 17), 1) != 17) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t2_float_param", - "package main;\n" - "fn fsum(t: (f64, i64)) f64 = {\n" - " return t.0 + (t.1: f64);\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (f64, i64) = (2.5, 4);\n" - " if (fsum(t) != 6.5) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t2_unwrap_arg", - "package main;\n" - "fn mk() ((i64, i64) | nomem) = {\n" - " let a: i64 = 5;\n" - " let b: i64 = 6;\n" - " return (a, b);\n" - "};\n" - "fn sum(t: (i64, i64)) i64 = {\n" - " return t.0 + t.1;\n" - "};\n" - "export fn main() i32 = {\n" - " if (sum(mk()!) != 11) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* ken's >6-GP-pressure stress: 4 leading scalars + the 2-word - * tuple + a 7th GP word — the restage drain fills the INTEGER - * cursor past the tuple and the 7th word must still land in the - * stack arg class, not be eaten or shifted by the tuple's pops. */ - { "t2_gp_pressure", - "package main;\n" - "fn f(a: i64, b: i64, c: i64, d: i64, t: (i64, i64), e: i64) i64 = {\n" - " if (e != 60) { return -1; };\n" - " if (a != 1 || b != 2 || c != 3 || d != 4) { return -2; };\n" - " return t.0 * 100 + t.1;\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (i64, i64) = (7, 9);\n" - " if (f(1, 2, 3, 4, t, 60) != 709) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* a tuple arg mixed with a VARIADIC tail rides the same restage — - * positive pin of the variadic interaction (ken demand 2). */ - { "t2_variadic_after_tuple", - "package main;\n" - "fn g(t: (i64, i64), xs: i64...) i64 = {\n" - " let s: i64 = t.0 + t.1;\n" - " for (let x .. xs) { s += x; };\n" - " return s;\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (i64, i64) = (3, 4);\n" - " if (g(t, 1, 2) != 10) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* rule-7 (ken demand 1): a NESTED composite element (tuple-in- - * tuple here) occupies more than the one GP word the restage walk - * counts — pre-guard the checker accepted it and it ran WRONG - * (inner words skewed; wwstage SIGSEGV'd). #24 now outlaws the - * composite-element tuple TYPE at resolution (both stages), so the - * reject fires here before the later "tuple arg element kind - * unsupported" arg-pass loud — experr migrated to the #24 text. - * Full inline support deferred to task #60. */ - { "t2_reject_nested_elem_arg", - "package main;\n" - "fn f(t: ((i64, i64), i64)) i64 = {\n" - " return t.1;\n" - "};\n" - "export fn main() i32 = {\n" - " let inner: (i64, i64) = (1, 2);\n" - " let t: ((i64, i64), i64) = (inner, 9);\n" - " if (f(t) != 9) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "tuple element must be a scalar" }, - /* variadic-of-tuples ((i64,i64)...): `ts[0].0` is an indexed-slice - * tuple-element FIELD read — #121 leg (a) now resolves it (was LOUD - * "unsupported field-read shape"; the dispatch fired only for an - * N_IDENT base). Runs correct both stages + byte-id. */ - { "t2_variadic_of_tuples", - "package main;\n" - "fn first(ts: (i64, i64)...) i64 = {\n" - " if (len(ts) == 0) { return -1; };\n" - " return ts[0].0;\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (i64, i64) = (3, 4);\n" - " if (first(t) != 3) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_RUN, NULL }, - /* rule-7: a tuple arg from a source whose cgexpr does NOT fill - * the cursor (here a struct-field chain) dies loud — pre-C-t2 - * it fell to the scalar single-PUSHQ default silently. - * #49 note: the tuple field fills from an IDENT source - * (`t = tt`, the #49 fill-field copy) so the build reaches the - * pinned ARG reject. The previous tuple-LITERAL fill - * (`t = (1, 2)`) now louds EARLIER at the #49 fill arm - * ("aggregate field from a non-addressable source unwired") — - * pre-#49 it silently word0-filled and only the arg push - * loudened. */ - { "t2_reject_chain_arg", - "package main;\n" - "type holder = struct { t: (i64, i64) };\n" - "fn sum(t: (i64, i64)) i64 = {\n" - " return t.0 + t.1;\n" - "};\n" - "export fn main() i32 = {\n" - " let tt: (i64, i64) = (1, 2);\n" - " let h: holder = holder{ t = tt };\n" - " if (sum(h.t) != 3) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "tuple arg from unsupported source shape" }, - /* rule-7: an over-cap tuple ident arg louds at the slot-to-cursor - * cap (the #10 sret follow-up), never a partial push. */ - { "t2_reject_overcap_ident_arg", - "package main;\n" - "fn f(t: (str, str)) i64 = {\n" - " return (len(t.0) + len(t.1)): i64;\n" - "};\n" - "fn g() (str, str) = {\n" - " return (\"ab\", \"cde\");\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (str, str) = g();\n" - " if (f(t) != 5) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "tuple ident exceeds register-return ABI capacity" }, - /* fold-4 substrate pin (charset_range_item = [](u32,u32)): the - * tuple-in-slice consumer shape is LOUD today, both stages — - * wiring it is fold-4's prerequisite work, not a silent gap. */ - { "charset_slice_append_reject", - "package main;\n" - "export fn main() i32 = {\n" - " let s: [](u32, u32) = [];\n" - " append(s, (1, 2));\n" - " if (len(s) != 1) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "element kind unsupported" }, - - /* ---- C-t3 (#48): GLOBAL tuples. Pre-C-t3 the definition was - * SILENTLY skipped (let_emit_size 0 → no DATA, no diagnostic); - * cstage element reads then read BP-frame garbage (localfind→0) - * and wwstage mis-emitted the field index as a symbol - * (`MOVQ 0(SB), AX`). Now: slot-laid DATAW (+ DATAR str-element - * ptr patches), element reads + len(g.N) via LEAQ sym(SB); - * unsupported element inits and the whole-tuple-as-value shape - * die LOUD. ---- */ - { "t3_global_elem", - "package main;\n" - "let g: (str, i64) = (\"hello\", 9);\n" - "let h: (u32, u32) = (5, 6);\n" - "export fn main() i32 = {\n" - " if (len(g.0) != 5) { return 1; };\n" - " if (g.1 != 9) { return 2; };\n" - " if (h.0 != 5) { return 3; };\n" - " if (h.1 != 6) { return 4; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t3_reject_float_global", - "package main;\n" - "let g: (f64, i64) = (2.5, 4);\n" - "export fn main() i32 = {\n" - " if (g.1 != 4) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "unsupported element init (int/str literals only; rule 7)" }, - { "t3_reject_whole_value", - "package main;\n" - "let g: (i64, i64) = (3, 4);\n" - "export fn main() i32 = {\n" - " let q: (i64, i64) = g;\n" - " if (q.0 != 3) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "global tuple as a first-class value unwired" }, - /* pre-existing loud (no-regress pin): global tuple element WRITE. */ - { "t3_reject_global_write", - "package main;\n" - "let g: (i64, i64) = (3, 4);\n" - "export fn main() i32 = {\n" - " g.0 = 7;\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "unsupported assign target shape" }, - /* ---- Family C (#35/#46): tagged transport from DEREF / CAST / - * UNWRAP sources. Pre-fix every cell was silent-wrong (the source - * materialized as ONE scalar word — tag only; cs pushed stale - * regs, ww stored 0/garbage; ken f35 + ken37v D3a/D3b). The fix - * extends the #37 mem-based predicate to ANY-size deref (the - * pointer value IS the box address), peels tagged→tagged casts at - * the transport consumers, and shifts the nested-box payload on - * `?`/`!` unwrap. Every K_RUN row also pins cs==ww byte-id. */ - { "fc_arg_deref_16", /* ken f35 exact shape */ - "package main;\n" - "fn take(e: (void | size)) size = {\n" - " if (!(e is size)) { return 99; };\n" - " return e as size;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: (void | size) = 7: size;\n" - " let p: *(void | size) = &v;\n" - " if (take(*p) != 7) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_let_match_16", /* ken D3a exact shape */ - "package main;\n" - "type un16 = (void | size);\n" - "export fn main() i32 = {\n" - " let v: un16 = 5: size;\n" - " let p: *un16 = &v;\n" - " let w = *p;\n" - " if (!(w is size)) { return 1; };\n" - " match (*p) {\n" - " case let s: size => { if (s != 5) { return 2; }; };\n" - " case void => { return 3; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_let_match_32str", /* ken D3b exact shape (at-cap, str payload) */ - "package main;\n" - "type un32 = (void | str);\n" - "export fn main() i32 = {\n" - " let v: un32 = \"atcap\";\n" - " let p: *un32 = &v;\n" - " let w = *p;\n" - " if (!(w is str)) { return 1; };\n" - " match (*p) {\n" - " case let s: str => { if (s.len != 5) { return 2; }; };\n" - " case void => { return 3; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_asg_16", /* ASSIGN (not let-init) consumer */ - "package main;\n" - "type un16 = (void | size);\n" - "export fn main() i32 = {\n" - " let v: un16 = 5: size;\n" - " let p: *un16 = &v;\n" - " let w: un16 = void;\n" - " w = *p;\n" - " if (!(w is size)) { return 1; };\n" - " if ((w as size) != 5) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_ret_16", /* return *p — the ww cgreturn fall-through wrapped the POINTER */ - "package main;\n" - "type un16 = (void | size);\n" - "fn get(p: *un16) un16 = {\n" - " return *p;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: un16 = 5: size;\n" - " let w = get(&v);\n" - " if (!(w is size)) { return 1; };\n" - " if ((w as size) != 5) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_is_as_16", /* direct is/as consumers */ - "package main;\n" - "type un16 = (void | size);\n" - "export fn main() i32 = {\n" - " let v: un16 = 5: size;\n" - " let p: *un16 = &v;\n" - " if (!((*p) is size)) { return 1; };\n" - " if (((*p) as size) != 5) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_as_32str", /* as on at-cap str payload */ - "package main;\n" - "type un32 = (void | str);\n" - "export fn main() i32 = {\n" - " let v: un32 = \"atcap\";\n" - " let p: *un32 = &v;\n" - " let s = (*p) as str;\n" - " if (s.len != 5) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_arg_32str", /* arg push, at-cap str payload */ - "package main;\n" - "type un32 = (void | str);\n" - "fn take(e: un32) i32 = {\n" - " if (!(e is str)) { return 99; };\n" - " let s = e as str;\n" - " if (s.len != 5) { return 98; };\n" - " return 0;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: un32 = \"atcap\";\n" - " let p: *un32 = &v;\n" - " return take(*p);\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_widen_16to24", /* deref into a WIDER union (remap rides) */ - "package main;\n" - "type un16 = (void | size);\n" - "type un3 = (void | size | str);\n" - "export fn main() i32 = {\n" - " let v: un16 = 5: size;\n" - " let p: *un16 = &v;\n" - " let w: un3 = *p;\n" - " if (!(w is size)) { return 1; };\n" - " if ((w as size) != 5) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_struct24_neighbor", /* struct payload + neighbor-guard locals */ - "package main;\n" - "type s2 = struct { a: size, b: size };\n" - "type un24 = (void | s2);\n" - "export fn main() i32 = {\n" - " let lo: size = 111;\n" - " let v: un24 = s2 { a = 5, b = 6 };\n" - " let hi: size = 222;\n" - " let p: *un24 = &v;\n" - " let w = *p;\n" - " if (!(w is s2)) { return 1; };\n" - " match (w) {\n" - " case let s: s2 => { if (s.a != 5 || s.b != 6) { return 2; }; };\n" - " case void => { return 3; };\n" - " };\n" - " if (lo != 111) { return 4; };\n" - " if (hi != 222) { return 5; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_56_memread", /* >32B deref — the #37 loud flips to working */ - "package main;\n" - "type big = struct { a: u64, b: u64, c: u64, d: u64, e: u64, f: u64 };\n" - "type un56 = (void | big);\n" - "export fn main() i32 = {\n" - " let v: un56 = big { a = 1u64, b = 2u64, c = 3u64, d = 4u64, e = 5u64, f = 6u64 };\n" - " let p: *un56 = &v;\n" - " let w = *p;\n" - " if (!(w is big)) { return 1; };\n" - " match (*p) {\n" - " case let s: big => { if (s.a != 1u64 || s.f != 6u64) { return 2; }; };\n" - " case void => { return 3; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_cast_ident_arg", /* reviewer-22's cast shape: identity cast at ARG */ - "package main;\n" - "type un16 = (void | size);\n" - "fn take(e: un16) size = {\n" - " if (!(e is size)) { return 99; };\n" - " return e as size;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: un16 = 7: size;\n" - " if (take((v: un16)) != 7) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_cast_widen_let", /* widening cast at let — payload checked (was garbage-payload) */ - "package main;\n" - "type un16 = (void | size);\n" - "type un3 = (void | size | str);\n" - "export fn main() i32 = {\n" - " let v: un16 = 5: size;\n" - " let w: un3 = (v: un3);\n" - " if (!(w is size)) { return 1; };\n" - " if ((w as size) != 5) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_unwrap_tagged_let", /* ken unw16 (success-first): `?` success variant is itself tagged */ - "package main;\n" - "type un16 = (void | size);\n" - "type err = !void;\n" - "fn get() (un16 | err) = {\n" - " let v: un16 = 7: size;\n" - " return v;\n" - "};\n" - "fn use() (size | err) = {\n" - " let w = get()?;\n" - " if (!(w is size)) { return 99; };\n" - " return w as size;\n" - "};\n" - "export fn main() i32 = {\n" - " match (use()) {\n" - " case let s: size => { if (s != 7) { return 1; }; };\n" - " case err => { return 2; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_unwrap_ident", /* `v?` on an IDENT — was a silent word0 unwrap */ - "package main;\n" - "type err = !void;\n" - "fn use(v: (size | err)) (size | err) = {\n" - " let w = v?;\n" - " return w: size;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: (size | err) = 7: size;\n" - " match (use(v)) {\n" - " case let s: size => { if (s != 7) { return 1; }; };\n" - " case err => { return 2; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_unwrap_deref", /* `(*p)?` — mem-based unwrap, was silent garbage */ - "package main;\n" - "type err = !void;\n" - "fn use(p: *(size | err)) (size | err) = {\n" - " let x = (*p)?;\n" - " return x: size;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: (size | err) = 7: size;\n" - " match (use(&v)) {\n" - " case let s: size => { if (s != 7) { return 1; }; };\n" - " case err => { return 2; };\n" - " };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_cast_deref_arg", /* ken gC1 second half: identity cast WRAPPING a deref at ARG */ - "package main;\n" - "type un16 = (void | size);\n" - "fn take(e: un16) size = {\n" - " if (!(e is size)) { return 99; };\n" - " return e as size;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: un16 = 7: size;\n" - " let p: *un16 = &v;\n" - " if (take(((*p): un16)) != 7) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_void_16", /* ken gC2: VOID variant through deref — tag-0 path at match/is/let */ - "package main;\n" - "type un16 = (void | size);\n" - "export fn main() i32 = {\n" - " let v: un16 = void;\n" - " let p: *un16 = &v;\n" - " match (*p) {\n" - " case let s: size => { return 1; };\n" - " case void => { };\n" - " };\n" - " if ((*p) is size) { return 2; };\n" - " let w = *p;\n" - " if (!(w is void)) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_slice_elem_16", /* ken gC3: pointer INTO a slice element, size + void variants */ - "package main;\n" - "type un16 = (void | size);\n" - "export fn main() i32 = {\n" - " let xs: []un16 = [];\n" - " append(xs, 11: size);\n" - " append(xs, void);\n" - " let p: *un16 = &xs[0];\n" - " match (*p) {\n" - " case let s: size => { if (s != 11) { return 1; }; };\n" - " case void => { return 2; };\n" - " };\n" - " let q: *un16 = &xs[1];\n" - " if (!((*q) is void)) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_arg_40", /* 33-48B deref ARG — the mem-push leg the #37-era loud guarded - * (silent word0 at base for deref; payload words checked late-first) */ - "package main;\n" - "type big4 = struct { a: u64, b: u64, c: u64, d: u64 };\n" - "type un40 = (void | big4);\n" - "fn take(e: un40) u64 = {\n" - " match (e) {\n" - " case let g: big4 => {\n" - " if (g.b != 2u64 || g.c != 3u64) { return 90u64; };\n" - " return g.a + g.d;\n" - " };\n" - " case void => { return 0u64; };\n" - " };\n" - "};\n" - "export fn main() i32 = {\n" - " let v: un40 = big4 { a = 30u64, b = 2u64, c = 3u64, d = 12u64 };\n" - " let p: *un40 = &v;\n" - " if (take(*p) != 42u64) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_deref_arg_56", /* ken gC6: >48B deref ARG — the MEMORY-class memarg place leg - * (already place-resolved at base; regression pin, not a flip) */ - "package main;\n" - "type big = struct { a: u64, b: u64, c: u64, d: u64, e: u64, f: u64 };\n" - "type un56 = (void | big);\n" - "fn take(e: un56) u64 = {\n" - " match (e) {\n" - " case let g: big => {\n" - " if (g.b != 2u64 || g.e != 5u64) { return 90u64; };\n" - " return g.a + g.f;\n" - " };\n" - " case void => { return 0u64; };\n" - " };\n" - "};\n" - "export fn main() i32 = {\n" - " let v: un56 = big { a = 30u64, b = 2u64, c = 3u64, d = 4u64, e = 5u64, f = 12u64 };\n" - " let p: *un56 = &v;\n" - " if (take(*p) != 42u64) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "fc_arg_balance_multi", /* POP-side balance: tagged-deref arg is NEITHER first nor last - * (the original #35 symptom was 1-push-N-pops); called TWICE so a - * persistent SP imbalance breaks the second call or the epilogue */ - "package main;\n" - "type un16 = (void | size);\n" - "fn take(a: size, e: un16, b: size) size = {\n" - " if (a != 100) { return 90; };\n" - " if (!(e is size)) { return 91; };\n" - " if ((e as size) != 7) { return 92; };\n" - " if (b != 200) { return 93; };\n" - " return a + (e as size) + b;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: un16 = 7: size;\n" - " let p: *un16 = &v;\n" - " if (take(100, *p, 200) != 307) { return 1; };\n" - " if (take(100, *p, 200) != 307) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* Family C rule-7 louds: unwired source shapes die loud, not word0. */ - { "fc_loud_global_unwrap", - "package main;\n" - "type err = !void;\n" - "let g: (size | err) = 7: size;\n" - "fn use() (size | err) = {\n" - " let x = g?;\n" - " return x: size;\n" - "};\n" - "export fn main() i32 = {\n" - " match (use()) {\n" - " case let s: size => { if (s != 7) { return 1; }; };\n" - " case err => { return 2; };\n" - " };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "on a global tagged ident unwired" }, - { "fc_loud_cast_third_union", - "package main;\n" - "type un3 = (void | size | str);\n" - "type un4 = (void | size | str | *u8);\n" - "export fn main() i32 = {\n" - " let w: un4 = (5: size): un3;\n" - " if (!(w is size)) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "tagged cast source shape unwired" }, - - /* ---- task #44: INFERRED-let tuple literal — cstage's N_TUPLE - * expr type carried size 0 (only the annotated N_TTUPLE route - * computed the slot layout), so cgen's N_LET planted the local - * at offset 0: the element stores landed on the saved BP/RIP - * and main SEGFAULTED on RET. wwstage (exprtype N_TUPLE → - * tinfofornode) was runtime-correct — the rare cstage-is-the-bug - * polarity (#263 corollary). Every row below segfaulted (or, for - * the arg row, link-failed on the off==0 global fallback) at - * master e8977a4 cstage; all are byte-id post-fix. ---- */ - { "t44_inferred_let_cast", - "package main;\n" - "export fn main() i32 = {\n" - " let t = (4: size, 2: size);\n" - " if (t.0 != 4) { return 1; };\n" - " if (t.1 != 2) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* bare untyped-int elements; the decl alone (no .N read) already - * smashed the frame at master — the noread shape rides the same - * row via the early stores. */ - { "t44_inferred_let_bare", - "package main;\n" - "export fn main() i32 = {\n" - " let t = (4, 2);\n" - " if (t.0 != 4) { return 1; };\n" - " if (t.1 != 2) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t44_inferred_let_mixed3", - "package main;\n" - "export fn main() i32 = {\n" - " let t = (4: size, 2, 36: size);\n" - " if (t.0 + t.2 != 40) { return 1; };\n" - " if (t.1 != 2) { return 2; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t44_inferred_let_float", - "package main;\n" - "export fn main() i32 = {\n" - " let t = (1.5, 2.5);\n" - " if (t.0 + t.1 != 4.0) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* destructure FROM the inferred local — the mlet source ident - * resolved to the 0-size slot at offset 0 (same root). */ - { "t44_inferred_destructure", - "package main;\n" - "export fn main() i32 = {\n" - " let t = (40: size, 2: size);\n" - " let (a, b) = t;\n" - " if (a + b != 42) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* inferred local as a tuple CALL ARG — at master the off==0 local - * fell to the global-symbol path: `w6l: undefined reference to - * 't'` (link-fail, not segfault — same root, different death). */ - { "t44_inferred_arg", - "package main;\n" - "fn add(t: (size, size)) size = {\n" - " return t.0 + t.1;\n" - "};\n" - "export fn main() i32 = {\n" - " let t = (40: size, 2: size);\n" - " if (add(t) != 42) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* nested tuple element: slot rule gives an inner tuple one 8B - * eightbyte (tuple_eslot parity) — pins the outer layout. */ - /* #24: an INFERRED nested-tuple element `((4,2),36)` drops the inner - * tuple on construction (build-proved 76 not 42 reading t.0) and is now - * outlawed on BOTH stages — same DISP-B reject as 832's tuple_nested_- - * exact, but via the inferred-literal path (check.c N_TUPLE expr + - * wwstage tinfofornode). Migrated K_RUN -> K_BUILDERR. Full inline - * support deferred to task #60. */ - { "t44_inferred_nested", - "package main;\n" - "export fn main() i32 = {\n" - " let t = ((4, 2), 36);\n" - " if (t.1 != 36) { return 1; };\n" - " return 0;\n" - "};\n", 0, K_BUILDERR, "tuple element must be a scalar" }, - /* #57: an IN-CAP tuple literal's cursor fill keyed each element on - * its STAMPED type (element-constructed, check.c N_TUPLE), so a - * declared-TAGGED element from a concrete rvalue (`(5: size, 9)`) - * counted ONE word and skipped the widen — 2 words sent against the - * receiver's declared 3-word walk; every later element read garbage - * (both stages, byte-id, gate-blind; ken /tmp/ken57). Rows: the - * return position (named + inline union spelling, t.N + destructure - * readback), the let-literal twin, tagged SECOND element (shift - * direction), float payload (the SSE-row gate: a (void|f64) box - * rides INTEGER eightbytes), bare-untyped payload (rides the #33 - * chooser through the new widen wire), the ident no-regress anchor, - * and the still-loud CALL-elem tripwire (#41). */ - { "t57_ret_cast_elem", - "package main;\n" - "type un16 = (void | size);\n" - "fn mk() (un16, size) = {\n" - " return (5: size, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " if (t.1 != 9) { return 1; };\n" - " let (a, b) = mk();\n" - " if (!(a is size)) { return 2; };\n" - " if (a as size != 5) { return 3; };\n" - " if (b != 9) { return 4; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t57_ret_inline_union", - "package main;\n" - "fn mk() ((void | size), size) = {\n" - " return (5: size, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let (a, b) = mk();\n" - " if (!(a is size)) { return 1; };\n" - " if (b != 9) { return 2; };\n" - " if (a as size != 5) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t57_let_literal", - "package main;\n" - "type un16 = (void | size);\n" - "export fn main() i32 = {\n" - " let t: (un16, size) = (5: size, 9);\n" - " if (t.1 != 9) { return 1; };\n" - " if (!(t.0 is size)) { return 2; };\n" - " if (t.0 as size != 5) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t57_tagged_second_elem", - "package main;\n" - "type un16 = (void | size);\n" - "fn mk2() (size, un16) = {\n" - " return (3, 7: size);\n" - "};\n" - "export fn main() i32 = {\n" - " let (a, b) = mk2();\n" - " if (a != 3) { return 1; };\n" - " if (!(b is size)) { return 2; };\n" - " if (b as size != 7) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t57_float_payload", - "package main;\n" - "type unf = (void | f64);\n" - "fn mkf() (unf, size) = {\n" - " return (1.5, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let (a, b) = mkf();\n" - " if (b != 9) { return 1; };\n" - " if (!(a is f64)) { return 2; };\n" - " if (a as f64 != 1.5) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* ken's adversarial shapes (k57a/b/c, /tmp/ken57) as rows: the - * tagged-MID elem shifts the cursor on BOTH sides of the box; two - * tagged rvalue elems (incl. a VOID rvalue) reuse the shared scratch - * sequentially; a true SSE elem (plain f64) coexisting with a - * declared-tagged box pins the gate splitting the rows. */ - { "t57_tagged_mid_elem", - "package main;\n" - "type un = (void | size);\n" - "fn mk3() (size, un, size) = {\n" - " return (1, 5: size, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let (a, b, c) = mk3();\n" - " if (a != 1) { return 1; };\n" - " if (!(b is size)) { return 2; };\n" - " if (b as size != 5) { return 3; };\n" - " if (c != 9) { return 4; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t57_two_tagged_void_elem", - "package main;\n" - "type un = (void | size);\n" - "fn mk() (un, un) = {\n" - " return (5: size, void);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " if (!(t.0 is size)) { return 1; };\n" - " if (t.0 as size != 5) { return 2; };\n" - " if (!(t.1 is void)) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t57_sse_coexist", - "package main;\n" - "type un = (void | size);\n" - "fn mk() (f64, un) = {\n" - " return (0.5, 7: size);\n" - "};\n" - "export fn main() i32 = {\n" - " let (x, e) = mk();\n" - " if (x * 4.0 != 2.0) { return 1; };\n" - " if (!(e is size)) { return 2; };\n" - " if (e as size != 7) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t57_bare_untyped_payload", - "package main;\n" - "type un16 = (void | size);\n" - "fn mkb() (un16, size) = {\n" - " return (5, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let (a, b) = mkb();\n" - " if (!(a is size)) { return 1; };\n" - " if (a as size != 5) { return 2; };\n" - " if (b != 9) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t57_ident_no_regress", - "package main;\n" - "type un16 = (void | size);\n" - "fn mk() (un16, size) = {\n" - " let e: un16 = 5: size;\n" - " return (e, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " if (t.1 != 9) { return 1; };\n" - " if (!(t.0 is size)) { return 2; };\n" - " if (t.0 as size != 5) { return 3; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - { "t57_loud_call_elem", - "package main;\n" - "type un16 = (void | size);\n" - "fn mke() un16 = {\n" - " return 5: size;\n" - "};\n" - "export fn main() i32 = {\n" - " let t: (un16, size) = (mke(), 9);\n" - " if (t.1 != 9) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "tagged tuple element from a non-local source shape unwired" }, - /* the NEW #57 loud: an in-cap tagged->tagged SUBSET elem (stamped - * eslot 16 into declared eslot 24) needs a tag remap on the way into - * the slot — pinned loud both stages until the #23/#40 widening - * family wires it. (big,size) = 3+1 GP words, in-cap by GPCAP. */ - { "t57_loud_remap", - "package main;\n" - "type st16 = struct { a: size, b: size };\n" - "type small = (void | u8);\n" - "type big = (void | u8 | st16);\n" - "fn mk() (big, size) = {\n" - " let s: small = 5u8;\n" - " return (s, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " if (t.1 != 9) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "widening into a wider declared union slot needs a tag remap" }, - /* ken k57d: the in-cap/over-cap BOUNDARY — an over-cap - * (un,un,size,size) return with concrete rvalue tagged elems must - * route to the sret arm's #22b ident-only loud-stop, not slip into - * the (now declared-keyed) in-cap path: pins that the declared - * count and the over-cap classify (cg_sret_retsize) agree. */ - { "t57_loud_overcap_boundary", - "package main;\n" - "type un = (void | size);\n" - "fn mk() (un, un, size, size) = {\n" - " return (5: size, void, 8, 9);\n" - "};\n" - "export fn main() i32 = {\n" - " let t = mk();\n" - " if (t.2 != 8) { return 1; };\n" - " return 0;\n" - "};\n", 0, - K_BUILDERR, "tagged element in an over-cap (sret) tuple return" }, -}; - -/* 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]; - char rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/tsl_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tsl_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tsl_%d_%d", tmpdir, getpid(), i); - snprintf(errf, sizeof errf, "%s/err", tmpdir); - 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/tsl_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/tsl_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/tsl_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, "tuple_slot_layout: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("tuple_slot_layout: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/944_nonlit_tuple_widen_run.c b/test/wcc/944_nonlit_tuple_widen_run.c deleted file mode 100644 index 857b89f8..00000000 --- a/test/wcc/944_nonlit_tuple_widen_run.c +++ /dev/null @@ -1,309 +0,0 @@ -/* - * 944_nonlit_tuple_widen_run — 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 (N_TUPLE / N_CAST-of-N_TUPLE) - * 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. This blocked fold-6's - * `append(charsets[...], charclass_map[cc_idx])` (the INDEX shape). - * - * THE FIX (align-BOTH; both stages were loud, so there is NO runtime - * reference and byte-id is BLIND — #263). A new arm resolves the source - * ADDRESS through the existing place-address spine (cgplaceaddr — the same - * one &base[i] / *p / ident use) into SI, then BLOCK-COPIES sum(tuple_eslot) - * bytes (== the tuple type's table size under the 8B-slot layout) from the - * source address into the box payload (write_off+8), and stamps the variant - * tag. The source-tuple-in-memory layout already EQUALS the box-payload - * layout (same tuple_eslot strides the literal loop fills), so it is a flat - * address block-copy — no re-slotting, and a tagged element rides over as - * its already-built box. One mechanism closes the addressable trio. - * - * SCOPE (kept loud, out of #116): a CALL/sret tuple result (the tuple sits - * at the sret address, #40-kin) and struct-field / array-literal-element - * tuple sources (loud EARLIER at their construction, #49 / #270-1c). - * - * Because byte-id is blind here, the RUNTIME read-back is the correctness - * net (rob's obligation): EACH covered shape (ident / index / deref) builds - * the box from its non-literal tuple source, match-extracts the variant, and - * ASSERTS the payload survived — `len(x.0)` proves the str header rode over, - * `(*x.1)(arg)` indirect-calls the fn-ptr element and checks its result - * (drew's P3/P4 read-back). The tuple-LITERAL row is the already-green - * regression guard. All four rows confirmed to LOUD on base da30f10. - * - * The read-back deliberately avoids `str !=` (a pre-existing cs!=ww - * comparison-codegen divergence, rt_streq vs inline CMPQ — unrelated to - * #116) so the K_RUN byte-id check isolates the widen arm. - * - * NNN<950, self-contained (/tmp, no imports), so rule-14's selfhost-sibling - * race does not apply (903/940/945 precedent). - */ -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -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 */ - -struct row { const char *label; const char *src; int kind; int want; }; - -static const struct row rows[] = { - /* control (already green pre-#116): a CAST tuple LITERAL. Regression - * guard — the literal arm must keep emitting the same fill. */ - { "literal", - "package main;\n" - "fn fa(c: rune) bool = { return c == 'a'; };\n" - "type ci = (str, *fn(c: rune) bool);\n" - "type u = (rune | ci);\n" - "export fn main() i32 = {\n" - " let nm: str = \"ww\";\n" - " let cf: *fn(c: rune) bool = &fa;\n" - " let s: []u = [];\n" - " append(s, ((nm, cf): ci));\n" - " match (s[0]) {\n" - " case let r: rune => { return 90; };\n" - " case let x: ci => {\n" - " if ((len(x.0): i32) != 2) { return 1; };\n" - " if (!(*x.1)('a')) { return 2; };\n" - " return 0;\n" - " };\n" - " };\n" - " return 4;\n" - "};\n", K_RUN, 0 }, - /* IDENT: a tuple-typed local var as the widen source. */ - { "ident", - "package main;\n" - "fn fb(c: rune) bool = { return c == 'b'; };\n" - "type ci = (str, *fn(c: rune) bool);\n" - "type u = (rune | ci);\n" - "export fn main() i32 = {\n" - " let nm: str = \"zz\";\n" - " let cf: *fn(c: rune) bool = &fb;\n" - " let t: ci = (nm, cf);\n" - " let s: []u = [];\n" - " append(s, t);\n" - " match (s[0]) {\n" - " case let r: rune => { return 90; };\n" - " case let x: ci => {\n" - " if ((len(x.0): i32) != 2) { return 1; };\n" - " if (!(*x.1)('b')) { return 2; };\n" - " if ((*x.1)('a')) { return 3; };\n" - " return 0;\n" - " };\n" - " };\n" - " return 4;\n" - "};\n", K_RUN, 0 }, - /* INDEX: `tbl[i]` — the fold-6 shape. The array is filled by tuple- - * ident element stores (the foldable construction at HEAD), then the - * indexed element is the widen source. */ - { "index", - "package main;\n" - "fn fb(c: rune) bool = { return c == 'b'; };\n" - "fn fa(c: rune) bool = { return c == 'a'; };\n" - "type ci = (str, *fn(c: rune) bool);\n" - "type u = (rune | ci);\n" - "export fn main() i32 = {\n" - " let n0: str = \"aa\"; let c0: *fn(c: rune) bool = &fa;\n" - " let n1: str = \"zzz\"; let c1: *fn(c: rune) bool = &fb;\n" - " let t0: ci = (n0, c0);\n" - " let t1: ci = (n1, c1);\n" - " let tbl: [2]ci = [];\n" - " tbl[0] = t0;\n" - " tbl[1] = t1;\n" - " let s: []u = [];\n" - " append(s, tbl[1]);\n" - " match (s[0]) {\n" - " case let r: rune => { return 90; };\n" - " case let x: ci => {\n" - " if ((len(x.0): i32) != 3) { return 1; };\n" - " if (!(*x.1)('b')) { return 2; };\n" - " if ((*x.1)('a')) { return 3; };\n" - " return 0;\n" - " };\n" - " };\n" - " return 4;\n" - "};\n", K_RUN, 0 }, - /* DEREF: `*p` — a pointer to a tuple var as the widen source. */ - { "deref", - "package main;\n" - "fn fa(c: rune) bool = { return c == 'a'; };\n" - "type ci = (str, *fn(c: rune) bool);\n" - "type u = (rune | ci);\n" - "export fn main() i32 = {\n" - " let nm: str = \"qqq\";\n" - " let cf: *fn(c: rune) bool = &fa;\n" - " let t: ci = (nm, cf);\n" - " let p: *ci = &t;\n" - " let s: []u = [];\n" - " append(s, *p);\n" - " match (s[0]) {\n" - " case let r: rune => { return 90; };\n" - " case let x: ci => {\n" - " if ((len(x.0): i32) != 3) { return 1; };\n" - " if (!(*x.1)('a')) { return 2; };\n" - " if ((*x.1)('b')) { return 3; };\n" - " return 0;\n" - " };\n" - " };\n" - " return 4;\n" - "};\n", K_RUN, 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 tmpdir[96], src[128], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/ntw_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/ntw_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/ntw_%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>&1", - driver, outbin, src); - 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/ntw_bi_%d_%d.ww", getpid(), i); - snprintf(cs_s, sizeof cs_s, "/tmp/ntw_bi_%d_%d_cs.s", getpid(), i); - snprintf(ws_s, sizeof ws_s, "/tmp/ntw_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; - /* Single-file package — w6c compiles the .ww source directly (no - * imports, no combined.ww amalgam needed); diff the two emissions. */ - 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 " - "(#116 non-literal tuple widen regression)\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, "nonlit_tuple_widen: 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, "nonlit_tuple_widen: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("nonlit_tuple_widen: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/944_tuple_tagged_union_run.c b/test/wcc/944_tuple_tagged_union_run.c deleted file mode 100644 index 868cf43b..00000000 --- a/test/wcc/944_tuple_tagged_union_run.c +++ /dev/null @@ -1,310 +0,0 @@ -/* - * 944_tuple_tagged_union_run — project #47 gap-A: a tuple whose elements - * include a TAGGED union, boxed as a member of a tagged union, must - * CONSTRUCT + RETURN-STORE identically in both stages. - * - * A PLAIN tuple-in-union already worked (#242, 940_tuple_in_union); the - * blocker was specifically the TAGGED ELEMENT. cg_widen_tagged_store's - * tuple-in-union store walk had scalar/float/str/slice element arms but - * NO TY_TAGGED arm — it loud-stopped ("tagged element in a tuple-in-union - * payload unwired"). gap-A adds a RECURSIVE two-level widen: each tagged - * element is boxed into its tuple slot (inner tag@slot+0, payload@slot+8) - * by re-entering cg_widen_tagged_store per element, THEN the whole tuple is - * boxed (outer tag + 40B payload copy). The element's tuple-slot stride is - * its OWN box size (roundup8) — a (void|size) elem is 16B (2 slots), not - * 8B — mirroring the checker's N_TTUPLE accumulation (check.c:715-720 / - * check.ww:1640-1646). MEASURED at HEAD: tuple->size for - * ((void|size),(void|size),size) = 40 (16+16+8); union box = 48. - * - * Both stages get the SAME arm → byte-identical asm (rule 10). - * - * All rows are K_RUN: build+run exit==want on BOTH drivers AND cs==ww - * byte-id. The ret-shape rows (construct + return + `is error` consume, NO - * match case-arm) were the gap-A pin — wwstage's store correctness rides on - * byte-id with the cstage runtime reference. The full-b1c row (construct + - * return + MATCH case-arm + is/as elem reads) was cstage-only under gap-A - * because wwstage's checker rejected the tuple-with-tagged case pattern; - * #47 gap-B taught typeeqast the N_TTAGGED arm (cmd/wcc/type.c:288-300 - * TY_TAGGED twin), so wwstage now ACCEPTS it and the row is promoted to - * both-stage K_RUN — that full-b1c byte-id IS the gap-B correctness proof. - * - * NNN<950, self-contained (/tmp, no imports), so rule-14's selfhost-sibling - * race does not apply (903/940/945 precedent). Cites #47 gap-A. - */ -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -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 */ - -struct row { const char *label; const char *src; int kind; int want; }; - -static const struct row rows[] = { - /* ret-shape 3-tuple ((void|size),(void|size),size) | error: the - * parse_repetition shape. pr(0) builds the tuple (min=void tag0, - * max=5:size tag1, rep=7), pr(2) the error variant. Consumed via - * `is error` only — no match arm — so both stages build+run. The - * recursive inner box exercises BOTH inner tags (void + size). */ - { "ret_3tuple", - "package main;\n" - "type error = !str;\n" - "fn pr(sel: i32) (((void | size), (void | size), size) | error) = {\n" - " if (sel == 0) {\n" - " let min: (void | size) = void;\n" - " let max: (void | size) = 5: size;\n" - " return (min, max, 7: size);\n" - " };\n" - " return \"neg\": error;\n" - "};\n" - "export fn main() i32 = {\n" - " let a: (((void | size), (void | size), size) | error) = pr(0);\n" - " if (a is error) { return 1; };\n" - " let b: (((void | size), (void | size), size) | error) = pr(2);\n" - " if (b is error) { return 235; };\n" - " return 2;\n" - "};\n", K_RUN, 235 }, - /* ret-shape 2-tuple ((void|size), size) | error: min=5:size (tag1). - * Smaller box (8+16+8 = wait: 16+8 = 24B tuple, 32B union) exercises - * the same recursive arm at a different slot layout. */ - { "ret_2tuple", - "package main;\n" - "type error = !str;\n" - "fn pr(sel: i32) (((void | size), size) | error) = {\n" - " let min: (void | size) = 5: size;\n" - " return (min, 7: size);\n" - "};\n" - "export fn main() i32 = {\n" - " let a: (((void | size), size) | error) = pr(0);\n" - " if (a is error) { return 1; };\n" - " return 235;\n" - "};\n", K_RUN, 235 }, - /* scalar-elem control (regression guard): a PLAIN tuple-in-union with - * no tagged element still takes the scalar arm and stays byte-id. */ - { "scalar_ctrl", - "package main;\n" - "type error = !str;\n" - "fn pr(sel: i32) ((size, size, size) | error) = {\n" - " return (3: size, 4: size, 5: size);\n" - "};\n" - "export fn main() i32 = {\n" - " let a: ((size, size, size) | error) = pr(0);\n" - " if (a is error) { return 1; };\n" - " return 235;\n" - "};\n", K_RUN, 235 }, - /* full b1c (gap-B): construct + return + MATCH case-arm + is/as - * elem reads on the boxed tuple. pr(0): t.0 is void (+1), t.1 is size - * = 5 (+5), t.2*10 (+70) = 76. pr(1): t.0 is size==3 (+100), t.1 is - * void (+50), t.2 = 9 (+9) = 235. pr(2): error arm reads back the boxed - * error str (ptr+len) and checks its length (25). Reads back EVERY - * tuple-elem value the recursive store wrote. gap-B taught wwstage's - * typeeqast the N_TTAGGED arm, so its checker now ACCEPTS this tuple- - * with-tagged case pattern — both-stage K_RUN (build+run 235 + cs==ww - * byte-id) is the gap-B correctness proof. Self-contained (no import, - * per this file's invariant): the error payload is verified with the - * len() builtin over an ident-bound str (a cast-expr len() operand is - * not place-resolvable, #10/#41) rather than a strings.compare import. */ - { "b1c_full", - "package main;\n" - "type error = !str;\n" - "fn pr(sel: i32) (((void | size), (void | size), size) | error) = {\n" - " if (sel == 0) {\n" - " let min: (void | size) = void;\n" - " let max: (void | size) = 5: size;\n" - " return (min, max, 7: size);\n" - " };\n" - " if (sel == 1) {\n" - " let min: (void | size) = 3: size;\n" - " let max: (void | size) = void;\n" - " return (min, max, 9: size);\n" - " };\n" - " return \"Negative repetition count\": error;\n" - "};\n" - "export fn main() i32 = {\n" - " let acc: size = 0;\n" - " let a: (((void | size), (void | size), size) | error) = pr(0);\n" - " match (a) {\n" - " case let t: ((void | size), (void | size), size) => {\n" - " if (t.0 is void) { acc += 1; };\n" - " if (t.1 is size) { acc += t.1 as size; };\n" - " acc += t.2 * 10;\n" - " };\n" - " case let e: error => { return 250; };\n" - " };\n" - " let b: (((void | size), (void | size), size) | error) = pr(1);\n" - " match (b) {\n" - " case let t: ((void | size), (void | size), size) => {\n" - " if (t.0 is size && t.0 as size == 3) { acc += 100; };\n" - " if (t.1 is void) { acc += 50; };\n" - " acc += t.2;\n" - " };\n" - " case let e: error => { return 251; };\n" - " };\n" - " let c: (((void | size), (void | size), size) | error) = pr(2);\n" - " match (c) {\n" - " case let t: ((void | size), (void | size), size) => { return 252; };\n" - " case let e: error => {\n" - " let s: str = e: str;\n" - " if (len(s) != 25) { return 253; };\n" - " };\n" - " };\n" - " return (acc: i32);\n" - "};\n", K_RUN, 235 }, -}; - -/* build+run via a driver; returns 0 pass, nonzero fail. */ -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[128], tmpdir[96], outbin[128], errf[128], rmcmd[160], cmd[1024]; - - snprintf(tmpdir, sizeof tmpdir, "/tmp/ttu_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/ttu_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/ttu_%d_%d", tmpdir, getpid(), i); - snprintf(errf, sizeof errf, "%s/err", tmpdir); - 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) — K_RUN rows only. */ -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/ttu_bi_%d_%d.ww", getpid(), i); - snprintf(cs_s, sizeof cs_s, "/tmp/ttu_bi_%d_%d_cs.s", getpid(), i); - snprintf(ws_s, sizeof ws_s, "/tmp/ttu_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 " - "(#47 gap-A tuple-w/-tagged-elem store regression)\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, "tuple_tagged_union: 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++; - } - } - - /* cs==ww byte-id for every K_RUN row (gap-B promoted the full-b1c - * match-extract row, so all rows now emit wwstage .s). */ - if (access(w6c_ww, X_OK) == 0) { - for (int i = 0; i < n; i++) { - if (rows[i].kind != K_RUN) continue; - total++; - if (byteid(w6c, w6c_ww, &rows[i], i) != 0) fail++; - } - } - - if (fail) { - fprintf(stderr, "tuple_tagged_union: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("tuple_tagged_union: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/945_rvalue_tuple_destructure_run.c b/test/wcc/945_rvalue_tuple_destructure_run.c deleted file mode 100644 index 8471b565..00000000 --- a/test/wcc/945_rvalue_tuple_destructure_run.c +++ /dev/null @@ -1,236 +0,0 @@ -/* - * 945_rvalue_tuple_destructure_run — project #241: materialise an RVALUE - * tuple into the register cursor before a destructure / let bind. - * - * cgexpr could not produce a tuple VALUE: a literal `(a, b)` fell to the - * `cgexpr_int(0)` / `MOVQ $0, AX` default, a tuple-typed IDENT loaded only - * word0 into AX, and the `?`/`!` unwrap of a tuple-in-union payload lifted - * only word0->AX (stranding word1 in CX). So `let (x, y) = ` - * read GARBAGE past the first element (cstage), and the un-typed binder left - * wwstage's checker aborting (asserttyped). DANGEROUS gate-blind cs!=ww — the - * strconv blocker (`let (sign, u) = parseint(s, base)?`). - * - * Fix (both stages, byte-identical per rule 10): cgexpr packs an N_TUPLE - * literal and a tuple IDENT into the SysV register-return cursor (the SAME - * ABI a tuple-returning call leaves, which the cgmlet/cgmassign consumers - * already read); the ?/! unwrap shifts a tuple success payload down one - * integer reg past the tag. wwstage's checker recovers the popped match-arm - * binder type for a `yield ` (scope-free, via the arm's declared - * type) so the destructured binders stamp. - * - * Rows assert BOTH elements on BOTH drivers AND cs==ww byte-identical: - * literal_destructure `let (a, b) = (true, 11u64)` - * match_yield `let (sg, u) = match (mk()) { case let t => yield t }` - * trycall_strconv `let (sign, u) = parseint(base)?` (the strconv shape) - * - * NOT covered (separate root, deferred): single-var `let q = (true, 9u64)` - * then `q.N` rides #238 — the N_LET tuple-init sz==16||32 gate + the N_DOT - * tuple-field PACKED-offset reader vs tuple_store's 8B stride disagree for a - * narrow-first tuple. Out of scope for #241 (the rvalue-into-cursor fix); see - * task #6 / proj #238. - * - * 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 -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -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[] = { - { "literal_destructure", - "package main;\n" - "export fn main() i32 = {\n" - " let (a, b) = (true, 11u64);\n" - " if (b != 11u64) { return 1; };\n" - " if (!a) { return 2; };\n" - " return 0;\n" - "};\n", 0 }, - { "match_yield", - "package main;\n" - "type myerr = !void;\n" - "fn mk(x: u64, s: bool) ((bool, u64) | myerr) = { return (s, x); };\n" - "export fn main() i32 = {\n" - " let (sg, u) = match (mk(6u64, true)) {\n" - " case let t: (bool, u64) => yield t;\n" - " case myerr => { return 9; };\n" - " };\n" - " if (u != 6u64) { return 1; };\n" - " if (!sg) { return 2; };\n" - " return 0;\n" - "};\n", 0 }, - { "trycall_strconv", - "package main;\n" - "type invalid = !void;\n" - "fn parseint(base: int) ((bool, u64) | invalid) = {\n" - " let sg: bool = true;\n" - " let n: u64 = 42u64;\n" - " return (sg, n);\n" - "};\n" - "fn stoi(base: int) (u64 | invalid) = {\n" - " let (sign, u) = parseint(base)?;\n" - " if (!sign) { return 0u64; };\n" - " return u;\n" - "};\n" - "export fn main() i32 = {\n" - " match (stoi(10)) {\n" - " case let v: u64 => { if (v != 42u64) { return 1; }; };\n" - " case invalid => { return 2; };\n" - " };\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/rvtd_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/rvtd_%d_%d.ww", tmpdir, getpid(), i); - snprintf(errf, sizeof errf, "%s/err", tmpdir); - snprintf(outbin, sizeof outbin, "%s/rvtd_%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/rvtd_bi_%d_%d.ww", getpid(), i); - snprintf(cs_s, sizeof cs_s, "/tmp/rvtd_bi_%d_%d_cs.s", getpid(), i); - snprintf(ws_s, sizeof ws_s, "/tmp/rvtd_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, "rvalue_tuple_destructure: 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, "rvalue_tuple_destructure: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("rvalue_tuple_destructure: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/945_tuple_lit_declblind_run.c b/test/wcc/945_tuple_lit_declblind_run.c deleted file mode 100644 index 8ff70b57..00000000 --- a/test/wcc/945_tuple_lit_declblind_run.c +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 945_tuple_lit_declblind_run — #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) — still 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. - * - * Fix (both stages, byte-identical per rule 10): N_MASSIGN builds a DECLARED - * tuple type from the lvalue binding types and threads it into the cursor - * fill + receive (a `_` lvalue, never type-stamped, falls back to the rhs - * literal element type for its stride); the call-arg send/restage/drain key - * on the PARAM tuple type (the literal's element-constructed type undercounts - * a wide tagged box). The single residual generic cgexpr(N_TUPLE) arm is - * provably non-widening (the constructed type IS the governing type there). - * - * Every RUN 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. Rows (RUN unless marked ERR): - * massign_tagged `a,b=(7i64,99)` into a:(i32|i64); n==7 ⇒ 1, else 3; - * wrong arm ⇒ 0; garbage-tag fall-through ⇒ 2 (base). - * callarg_tagged `f((7i64,99))` param (ev,i64); match t.0, n==7 ⇒ 1. - * base: garbage tag ⇒ fall-through ⇒ 2. - * massign_blank `_,a=(99,7i64)` — `_` stride + named box widen; the box - * is element 1 (off the `_`-aligned cursor). n==7 ⇒ 1. - * base: word-0-only store corrupts a's tag ⇒ fall-through ⇒ - * 2 (the box's tag sits at word0; the lone scalar word - * overwrites it — verified on pristine abd97e6). - * callarg_drain `g((7i64, 35))` param (ev,i64); g returns (box payload) - * + t.1. This is the #68 DRAIN teeth (ken's verified probe - * shape): the param-aware drain must pop the box's 2 words - * AND the trailing i64 together. base: box is word-0 only - * so the i64 arm never fires (payload 0) ⇒ 0 + 35 = 35; - * post: 7 + 35 = 42. base 35 ≠ 42, and the +35 simultaneously - * proves the trailing arg still lands in its reg. - * nested_tuple_arg [ERR] `h(((1,2),3))` param ((i32,i32),i64): a NESTED- - * TUPLE tuple-arg element has no transport and stays rule-7 - * LOUD (the fix graduates ONLY a declared-tagged element; - * the TY_TUPLE/STRUCT/ARRAY arms are preserved). Asserts the - * build FAILS (nonzero) on BOTH drivers — locks the - * preserved loudness so a future widen-everything regresses. - * - * RUN rows: build+run on BOTH drivers AND cs==ww .s byte-identical (rule 10). - * NNN<950, self-contained (/tmp, no imports), so rule-14's selfhost-sibling - * race does not apply (903/940/945 precedent). - */ -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -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; -} - -/* builderr: the build must FAIL (rule-7 loud); RUN rows leave it 0. */ -struct row { const char *label; const char *src; int want; int builderr; }; - -static const struct row rows[] = { - { "massign_tagged", - "package main;\n" - "type ev = (i32 | i64);\n" - "export fn main() i32 = {\n" - " let a: ev = 5i32;\n" - " let b: i64 = 0;\n" - " a, b = (7i64, 99);\n" - " match (a) {\n" - " case let n: i64 => { if (n == 7) { return 1; }; return 3; };\n" - " case let m: i32 => { return 0; };\n" - " };\n" - " return 2;\n" - "};\n", 1, 0 }, - { "callarg_tagged", - "package main;\n" - "type ev = (i32 | i64);\n" - "fn f(t: (ev, i64)) i32 = {\n" - " match (t.0) {\n" - " case let n: i64 => { if (n == 7) { return 1; }; return 3; };\n" - " case let m: i32 => { return 0; };\n" - " };\n" - " return 2;\n" - "};\n" - "export fn main() i32 = {\n" - " return f((7i64, 99));\n" - "};\n", 1, 0 }, - { "massign_blank", - "package main;\n" - "type ev = (i32 | i64);\n" - "export fn main() i32 = {\n" - " let a: ev = 5i32;\n" - " _, a = (99, 7i64);\n" - " match (a) {\n" - " case let n: i64 => { if (n == 7) { return 1; }; return 3; };\n" - " case let m: i32 => { return 0; };\n" - " };\n" - " return 2;\n" - "};\n", 1, 0 }, - { "callarg_drain", - "package main;\n" - "type ev = (i32 | i64);\n" - "fn g(t: (ev, i64)) i32 = {\n" - " let bp: i64 = 0;\n" - " match (t.0) {\n" - " case let n: i64 => { bp = n; };\n" - " case let m: i32 => { bp = 0; };\n" - " };\n" - " return (bp + t.1): i32;\n" - "};\n" - "export fn main() i32 = {\n" - " return g((7i64, 35));\n" - "};\n", 42, 0 }, - { "nested_tuple_arg", - "package main;\n" - "fn h(t: ((i32, i32), i64)) i32 = {\n" - " return 0;\n" - "};\n" - "export fn main() i32 = {\n" - " return h(((1, 2), 3));\n" - "};\n", 0, 1 }, -}; - -/* 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/tldb_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tldb_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tldb_%d_%d", tmpdir, getpid(), i); - snprintf(errf, sizeof errf, "%s/err", tmpdir); - 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); - /* ERR row: the rule-7 loud arm must fire — build FAILS (nonzero). A - * clean build means the preserved loudness regressed. */ - if (r->builderr) { - int ok = (brc != 0); - if (!ok) - fprintf(stderr, "row[%s]: %s built clean, expected " - "rule-7 loud build-fail\n", r->label, driver); - 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 -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/tldb_bi_%d_%d.ww", getpid(), i); - snprintf(cs_s, sizeof cs_s, "/tmp/tldb_bi_%d_%d_cs.s", getpid(), i); - snprintf(ws_s, sizeof ws_s, "/tmp/tldb_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, "tuple_lit_declblind: 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++) { - if (rows[i].builderr) continue; /* no .s to compare */ - total++; - if (byteid(w6c, w6c_ww, &rows[i], i) != 0) fail++; - } - } - - if (fail) { - fprintf(stderr, "tuple_lit_declblind: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("tuple_lit_declblind: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/945_tuple_nary_destructure_run.c b/test/wcc/945_tuple_nary_destructure_run.c deleted file mode 100644 index 6d649265..00000000 --- a/test/wcc/945_tuple_nary_destructure_run.c +++ /dev/null @@ -1,346 +0,0 @@ -/* - * 945_tuple_nary_destructure_run — runtime coverage for 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 - * (ref/hare/rt/ensure.ha:4-8), a scalar rides 1. SEND and RECEIVE walk the - * SAME cursor so element->register agrees. This closes the pre-existing - * (scalar,slice) cs!=ww divergence by construction (slice no longer rides - * the scalar fallback) and unifies both stages on the positional layout. - * - * LOUD-STOP (rule 7): the register file holds only 4 eightbytes. A tuple - * whose elements sum to > 4 (e.g. ([]u8,[]u8)/(str,str) = 3+3 = 6) CANNOT - * be register-returned; the send site emits a COMPILE-TIME abort citing - * the return-ABI capacity (project #10), NOT a silent miscompile. Row F - * asserts that loud stop fires on BOTH stages: the build must FAIL *and* - * stderr must carry the cited diagnostic (rule 7 — loud, not a silent - * crash that an exit-code-only check would mistake for the stop). - * - * N_MLET vs N_MASSIGN: `let (a, s) = f()` is N_MLET (fresh bindings, slots - * sized from each element type). `a, s = f()` with a, s PRE-DECLARED is - * N_MASSIGN (reassignment, existing slots) — a retained ww-EXTENSION - * beyond Hare (Hare tuple-unpack is binding-only; ww keeps the Go/rob-pike - * multi-assign idiom). The 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 — deterministic - * fail-before. The N_MLET rows use cap!=len distinct values; a dropped - * cap reads fresh-slot garbage, ~never the asserted pair. - * - * Backing storage: each []u8 element borrows a str literal's .ptr (stable - * .rodata), so the header survives the producer's return without a - * use-after-return. str elements return a literal with a MUTATED .cap so - * cap!=len (a bare literal carries cap==len, hiding a dropped cap). - * - * Rows (cap!=len in every wide row so a dropped len OR cap is caught): - * A mlet_islice `let (a, s) = f()`, f()->(i64,[]u8). a=4,len=2,cap=5,'h'. - * B massign_islice`a, s = f()` predeclared+poisoned (len=9,cap=9). - * C mlet_istr `let (a, s) = f()`, f()->(i64,str). a=4,len=2,cap=7,'h'. - * D massign_istr `a, s = f()` predeclared+poisoned (len=4,cap=6). - * E str_control plain single-str return `let s = f()` f()->str — the - * non-tuple str path (cgen.c N_RETURN node_isstr branch) - * the fix leaves untouched; len=5,cap=9 regression guard. - * G massign_blank_wide `_, a = f()`, f()->([]u8,i64). The blank `_` - * rides a 3-word slot so a lands on R8 (the i64), not DX - * (slice.len). len=2,cap=5,i64=7 distinct (any desync !=7). - * F slice_slice_recv f()->([]u8,[]u8) returns (a,b) — 6 eightbytes > - * cap; the SEND sret's it (#10 Fold A) and the - * destructure RECEIVE copies all 3 words/element out - * of @sretscr (#10 Fold B). len AND cap of both halves - * asserted (cap!=len) so a dropped word is caught. - * - * Fold discriminators: A/B (slice) — the old str-only XOR was slice-blind, - * so the slice fell to the scalar-pair fallback and dropped len/cap; B's - * poison makes that deterministic. G (blank `_`) — cstage derived the - * receive width from the binding type, which is null for an unstamped `_`, - * so a wide `_` was mis-sized scalar and the cursor desynced (cstage read - * DX, wwstage R8); deriving width from the rhs tuple type fixes + aligns - * both stages. F (over-cap recv) — the pre-#10 code register-returned - * ([]u8,[]u8) with only the two .ptr words (built + ran WRONG); #10 Fold A - * loud-stopped it at the SEND, and Fold B now sret's + copies it out into - * the bindings, so it is a working round-trip (was a BUILDERR at Fold A). - * C/D/E are CONTROLS: the (i64,str) path was ALREADY 3-word - * under the old XOR, and the single-str return is a separate untouched - * branch — they confirm no regression. All 14 - * fixtures pass on both the cstage `ww` and wwstage `ww_ww` drivers with - * byte-identical asm (smoke-verified pre-commit; the formal fail-before - * revert-run is ken's tuple83 gate). NNN<950, self-contained (/tmp, no - * imports), so rule-14's selfhost-sibling race does not apply (mirrors the - * 941/942/943/944 precedent). - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -/* experr (builderr rows only): a stable substring of the loud-stop - * diagnostic. rule 7 — the over-capacity stop must be LOUD: the build must - * fail BECAUSE of the cited return-ABI diagnostic (project #10), not an - * incidental/silent error. NULL skips the message check. */ -struct row { const char *label; const char *src; int want; int builderr; - const char *experr; }; - -static const struct row rows[] = { - /* A — mlet_islice: `let (a, s) = f()`, f()->(i64,[]u8). Slice borrows - * the "hi" literal's .ptr (stable). len=2, cap=5 (distinct). */ - { "mlet_islice", - "fn mk() (i64, []u8) = {\n" - " let b: str = \"hi\";\n" - " let p: []u8; p.ptr = b.ptr; p.len = 2; p.cap = 5;\n" - " return (4i64, p);\n" - "};\n" - "export fn main() i32 = {\n" - " let (a, s) = mk();\n" - " if (a != 4) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (s.cap: i32 != 5) { return 3; };\n" - " if (s[0] != 104u8) { return 4; };\n" - " return 0;\n" - "};\n", - 0, 0, NULL }, - /* B — massign_islice: `a, s = f()` predeclared; s poisoned (len=9, - * cap=9) via the proven 3-word `s = q` let-init copy. A dropped - * len/cap leaves the poison 9; the fix lands 2 / 5. */ - { "massign_islice", - "fn mk() (i64, []u8) = {\n" - " let b: str = \"hi\";\n" - " let p: []u8; p.ptr = b.ptr; p.len = 2; p.cap = 5;\n" - " return (4i64, p);\n" - "};\n" - "export fn main() i32 = {\n" - " let qb: str = \"zzzz\";\n" - " let q: []u8; q.ptr = qb.ptr; q.len = 9; q.cap = 9;\n" - " let a: i64 = 0i64;\n" - " let s: []u8 = q;\n" - " a, s = mk();\n" - " if (a != 4) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (s.cap: i32 != 5) { return 3; };\n" - " if (s[0] != 104u8) { return 4; };\n" - " return 0;\n" - "};\n", - 0, 0, NULL }, - /* C — mlet_istr: `let (a, s) = f()`, f()->(i64,str). cap mutated to - * 7 so cap!=len(2). */ - { "mlet_istr", - "fn mk() (i64, str) = {\n" - " let p: str = \"hi\"; p.cap = 7i32;\n" - " return (4i64, p);\n" - "};\n" - "export fn main() i32 = {\n" - " let (a, s) = mk();\n" - " if (a != 4) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (s.cap: i32 != 7) { return 3; };\n" - " if (s[0] != 104u8) { return 4; };\n" - " return 0;\n" - "};\n", - 0, 0, NULL }, - /* D — massign_istr: `a, s = f()` predeclared; s poisoned (len=4, - * cap=6) via the proven 3-word `s = q` copy. */ - { "massign_istr", - "fn mk() (i64, str) = {\n" - " let p: str = \"hi\"; p.cap = 7i32;\n" - " return (4i64, p);\n" - "};\n" - "export fn main() i32 = {\n" - " let q: str = \"qqqq\"; q.cap = 6i32;\n" - " let a: i64 = 0i64;\n" - " let s: str = q;\n" - " a, s = mk();\n" - " if (a != 4) { return 1; };\n" - " if (s.len: i32 != 2) { return 2; };\n" - " if (s.cap: i32 != 7) { return 3; };\n" - " if (s[0] != 104u8) { return 4; };\n" - " return 0;\n" - "};\n", - 0, 0, NULL }, - /* E — str_control: plain single-str return, the non-tuple path the - * fix leaves untouched. cap mutated to 9 so cap!=len(5). */ - { "str_control", - "fn mk() str = {\n" - " let p: str = \"hello\"; p.cap = 9i32;\n" - " return p;\n" - "};\n" - "export fn main() i32 = {\n" - " let s: str = mk();\n" - " if (s.len: i32 != 5) { return 1; };\n" - " if (s.cap: i32 != 9) { return 2; };\n" - " if (s[0] != 104u8) { return 3; };\n" - " return 0;\n" - "};\n", - 0, 0, NULL }, - /* G — massign_blank_wide: `_, a = f()`, f()->([]u8,i64), wide-first - * blank. The `_` (N_IDENT, empty str) is never type-stamped by the - * checker, so cstage previously mis-sized it as a scalar (1 word) and - * the cursor desynced: a read DX (slice.len=2) instead of R8 (the - * i64). The fix reads element width from the rhs tuple type, so `_` - * consumes its 3-word slot and a lands on R8. len=2, cap=5, i64=7 all - * distinct so any desync misread (DX=2 / CX=5) is caught, never 7. */ - { "massign_blank_wide", - "fn mk() ([]u8, i64) = {\n" - " let b: str = \"hi\";\n" - " let p: []u8; p.ptr = b.ptr; p.len = 2; p.cap = 5;\n" - " return (p, 7i64);\n" - "};\n" - "export fn main() i32 = {\n" - " let a: i64 = 0i64;\n" - " _, a = mk();\n" - " if (a != 7) { return 1; };\n" - " return 0;\n" - "};\n", - 0, 0, NULL }, - /* F — slice_slice_recv: ([]u8,[]u8) = 6 eightbytes > 4 capacity. The - * SEND sret's the over-cap tuple (#10 Fold A) and the destructure - * RECEIVE copies all 3 words per element out of the @sretscr slot - * (#10 Fold B) — formerly a loud-stop, now a working round-trip. - * Asserts BOTH len AND cap of each half (cap != len) so a dropped - * word — the original register-return bug — is caught at runtime. */ - { "slice_slice_recv", - "fn mk() ([]u8, []u8) = {\n" - " let a: []u8; a.len = 1; a.cap = 5;\n" - " let b: []u8; b.len = 2; b.cap = 6;\n" - " return (a, b);\n" - "};\n" - "export fn main() i32 = {\n" - " let (x, y) = mk();\n" - " if (x.len: i32 != 1) { return 1; };\n" - " if (x.cap: i32 != 5) { return 2; };\n" - " if (y.len: i32 != 2) { return 3; };\n" - " if (y.cap: i32 != 6) { return 4; };\n" - " return 0;\n" - "};\n", - 0, 0, NULL }, -}; - -static int -file_contains(const char *path, const char *needle) -{ - FILE *f = fopen(path, "rb"); - if (!f) return 0; - char buf[8192]; - size_t n = fread(buf, 1, sizeof buf - 1, f); - fclose(f); - buf[n] = '\0'; - return strstr(buf, needle) != NULL; -} - -/* Returns 0 on pass, nonzero on fail. For builderr rows the build must - * FAIL and (if experr is set) the diagnostic must carry the loud-stop - * message; for normal rows the build must succeed and the binary's exit - * must equal r->want. */ -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/tuplenary_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tuplenary_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tuplenary_%d_%d", tmpdir, getpid(), i); - snprintf(errf, sizeof errf, "%s/err", tmpdir); - 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->builderr) { - int ok = (brc != 0) - && (r->experr == NULL || file_contains(errf, r->experr)); - if (!ok) - fprintf(stderr, - "row[%s]: %s expected loud-stop builderr (brc=%d)\n", - r->label, driver, 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); - return (got == r->want) ? 0 : (got ? got : 1); -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - char wdrv[640]; - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - - struct { const char *name; const char *path; int gated_on_existence; } - 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_on_existence - && access(drivers[d].path, X_OK) != 0) { - fprintf(stderr, - "tuple_nary_destructure_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int rc = run_driver(drivers[d].path, &rows[i], i); - total++; - if (rc != 0) { - fprintf(stderr, - "tuple_nary_destructure_run[%s][%s]: rc=%d\n", - drivers[d].name, rows[i].label, rc); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "tuple_nary_destructure_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("tuple_nary_destructure_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/947_tuple_index_read_run.c b/test/wcc/947_tuple_index_read_run.c deleted file mode 100644 index b9b20631..00000000 --- a/test/wcc/947_tuple_index_read_run.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * 947_tuple_index_read_run — #121: const-slice tuple-element READ. - * - * 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): - * - * (a) FIELD `tbl[i].N` (bound) — was LOUD both stages ("unsupported - * field-read shape": the tuple-field dispatch fired only for an - * N_IDENT base; an N_INDEX base fell to the read-resolver fatal). - * Now resolved via cgplaceaddr + the per-element-kind arms - * (str-triple / fn-8B). align-loud→both-accept-correct. - * - * (b) WHOLE `let e = tbl[i]` then read e.N — was SILENT both-wrong- - * IDENTICAL (#263, word0-only: the in-cap tuple receive read a - * register cursor the N_INDEX load never filled past word0, so - * str.len→0 and the *fn word was garbage). Now fills all gptotal - * cursor words from &tbl[i]. align-both-correct. - * - * (c) for-range over a module-global slice — LOUD-STOP both stages - * (the for-range spine has no global-base resolution; pre-fix it - * read saved-BP → SEGV with divergent cs≠ww asm). Filed as a #121 - * sibling; loud is byte-id-neutral + a pure improvement. - * - * POLARITY: leg (a) cstage is the byte-id ref (align-loud→accept); leg - * (b) byte-id is BLIND (#263) — both stages were identically truncated, - * so the runtime READ-BACK with DISTINCT-per-row values (str lengths - * differ; fns fa↔'a'/fz↔'z' differ) is the only net: a wrong/truncated - * word is CAUGHT, not masked. - * - * MUST use `[](str,*fn)`, NOT `[](str,int)` — the latter is #120 - * wwstage-checker-rejected ("let: not assignable") BEFORE the read runs. - * - * NNN<950, self-contained (/tmp, no imports) — rule-14's selfhost-sibling - * race does not apply (941/944/945/946 precedent). Every K_RUN row also - * pins cstage/wwstage asm byte-id. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -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; }; - -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[] = { - /* leg (a): direct `tbl[i].N` field read, bound then observed. - * str field (distinct lengths 2 vs 4) + fn field (distinct fns, - * each called back). Was LOUD both stages at base 761cfa4. */ - { "field_read", - "package main;\n" - "fn fa(c: rune) bool = { return c == 'a'; };\n" - "fn fz(c: rune) bool = { return c == 'z'; };\n" - "const tbl: [](str, *fn(c: rune) bool) = " - "[(\"ab\", &fa), (\"cdef\", &fz)];\n" - "export fn main() i32 = {\n" - " let s1 = tbl[1].0;\n" - " let s0 = tbl[0].0;\n" - " if (len(s1) != 4) { return 1; };\n" - " if (len(s0) != 2) { return 2; };\n" - " let f1 = tbl[1].1;\n" - " if (!(*f1)('z')) { return 3; };\n" - " if ((*f1)('a')) { return 4; };\n" - " let f0 = tbl[0].1;\n" - " if (!(*f0)('a')) { return 5; };\n" - " if ((*f0)('z')) { return 6; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* leg (b): whole-element bind then read e.0 (str.len, catches the - * w1→0 truncation) + e.1 (the *fn word, catches w3 garbage). - * DISTINCT per row — a truncated/wrong word is caught not masked. - * Was SILENT both-wrong-identical (#263) at base 761cfa4. */ - { "whole_element", - "package main;\n" - "fn fa(c: rune) bool = { return c == 'a'; };\n" - "fn fz(c: rune) bool = { return c == 'z'; };\n" - "const tbl: [](str, *fn(c: rune) bool) = " - "[(\"ab\", &fa), (\"cdef\", &fz)];\n" - "export fn main() i32 = {\n" - " let e = tbl[1];\n" - " if (len(e.0) != 4) { return 1; };\n" - " if (!(*e.1)('z')) { return 2; };\n" - " if ((*e.1)('a')) { return 3; };\n" - " let e0 = tbl[0];\n" - " if (len(e0.0) != 2) { return 4; };\n" - " if (!(*e0.1)('a')) { return 5; };\n" - " if ((*e0.1)('z')) { return 6; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* write-face of leg (b): a tuple-LITERAL store into an indexed - * element `a[i] = (3,4)`, read back whole. DISTINCT words per row - * (3≠4, 7≠9) so the pre-fix word0-only store (cgen.c:6249 fell to - * the scalar tail) is CAUGHT — t.1 would read 0. Both faces (this - * store + the leg-b read) must be correct or the round-trip fails; - * the read alone was green by stale-register luck (809 tuple_elem). */ - { "store_roundtrip", - "package main;\n" - "export fn main() i32 = {\n" - " let a: [2](u64, u64);\n" - " a[1] = (3u64, 4u64);\n" - " a[0] = (7u64, 9u64);\n" - " let t = a[1];\n" - " if (t.0 != 3) { return 1; };\n" - " if (t.1 != 4) { return 2; };\n" - " let s = a[0];\n" - " if (s.0 != 7) { return 3; };\n" - " if (s.1 != 9) { return 4; };\n" - " return 0;\n" - "};\n", 0, K_RUN, NULL }, - /* leg (c): for-range over a module-global slice LOUD-STOPS, - * symmetric both stages (segfault→compile-error, byte-id-neutral). - * `n += 1` (not len()) sidesteps the #26 len-vs-int checker reject - * so the cgen loud is what fires, on BOTH stages. */ - { "forrange_global_loud", - "package main;\n" - "fn fa(c: rune) bool = { return c == 'a'; };\n" - "const tbl: [](str, *fn(c: rune) bool) = " - "[(\"ab\", &fa), (\"cdef\", &fa)];\n" - "export fn main() i32 = {\n" - " let n: int = 0;\n" - " for (let e .. tbl) { n += 1; };\n" - " return n: i32;\n" - "};\n", 0, K_BUILDERR, - "for-range over a module-global" }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char tmpdir[96], src[160], errf[160], outbin[160], rmcmd[176], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/tir_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tir_%d_%d.ww", tmpdir, getpid(), i); - snprintf(errf, sizeof errf, "%s/tir_%d_e_%d", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tir_%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 (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; -} - -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/tir_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/tir_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/tir_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, "tuple_index_read: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("tuple_index_read: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/954_tuprecv_run.c b/test/wcc/954_tuprecv_run.c index 0c92ced2..3e94ed81 100644 --- a/test/wcc/954_tuprecv_run.c +++ b/test/wcc/954_tuprecv_run.c @@ -1,43 +1,20 @@ /* - * 954_tuprecv_run — runtime + byte-id regression net for #102: the 16B - * whole-tuple-from-call receive (`let t = mk()`, then field reads t.0 / - * t.1). + * 954_tuprecv_run — carrier-split residue of #102 (16B whole-tuple-from-call + * receive). The VALUE rows and the cs==ww .s byte-id dimension migrated to the + * fold-2 row-table test/lang/tuprecv_test.ww (cstage run via test-lang + cs==ww + * via test-lang-byteid). What CANNOT ride a value/byte-id @test is the third + * dimension the old driver also carried: a tool-OUTPUT assertion that w6c_ww + * emits NO `asserttyped:` diagnostic on the un-annotated float-destructure + * binding `let (f, i) = mk()` — the #121 A-narrow stamp net. That is the same + * category as the Class-A asm-presence twins the Fam-7 commit deliberately KEPT + * (a diagnostic-observer grep, not a value or byte-id check), so it stays here + * as a minimal retained pin rather than being silently dropped (drew ruling, + * #5-C2). * - * 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 (`MOVQ AX, off(BP)`) and - * never spilled word1 (the DX eightbyte) — silent loss of t.1. cstage - * has the branch (cmd/w6c/cgen.c:6652: spill AX -> off+0 AND DX -> - * off+8). The fix mirrors it in cglet. - * - * NOTE on the diagnosis: this is NOT a tupstore cursor off-by-one, and - * it is NOT f64-specific. The destructure form `let (a,b) = mk()` - * (cgmlet + tupstore cursor) was already byte-id; only the whole-tuple - * N_LET receive dropped word1. An f64 element only made it visible - * first, but the all-integer (i64,i64) receive dropped DX too — so the - * "all-integer control" row below is ALSO a fix row (it diverges on - * master 60c3e51, byte-id after the fix), not a pure no-op control. - * - * THIS TEST MUST CATCH A WWSTAGE-ONLY DIVERGENCE. cstage is correct - * before and after, so a cstage-only probe is gate-blind. Each row - * carries BOTH dimensions (modelled on 953_f64crossmod_run): - * (a) cstage `ww build` + run, asserting the exit code — pins the asm - * both stages converge on as runtime-correct. - * (b) w6c vs w6c_ww `.s` cmp — FAILS if the stages diverge. On master - * 60c3e51 (pre-fix) the three scalar-pair single-var rows each - * diverge by exactly one dropped `MOVQ DX, -8(BP)`; post-fix all - * are byte-identical. - * - * Three CONTROL rows pin that the fix touches nothing else: the - * destructure form `let (a,b) = mk()` (cgmlet + tupstore cursor), a - * 32B wide/str-element single-var tuple, and a 16B struct{i64,i64} - * single-var receive — all stay byte-id both pre- and post-fix. The fix - * is in cglet/N_LET, gated on sz==16 AND rettupleof != nil; the struct - * row is the over-catch guard: sz IS 16 but rettupleof returns nil for a - * non-tuple return, so the new branch must NOT hijack the struct's - * generic store. Without that guard a struct receive would route through - * the AX/DX tuple spill and miscompile any struct whose layout differs - * from {word0,word1}. + * Non-vacuous: pre-stamp (HEAD before #121) w6c_ww fired `asserttyped` on the + * f64 binding ident (nil n.type_); post-fix the binder carries the checker + * stamp and the diagnostic is absent. wwstage-only — cstage's check.c has no ww + * asserttyped walker, so the pin invokes w6c_ww directly. */ #include #include @@ -54,100 +31,14 @@ runwait(const char *cmd) return -1; } -struct row { const char *label; const char *src; int want_exit; int chk_stamped; }; - -static const struct row rows[] = { - /* mixed (f64, i64): f=2.5 -> i32 2, i=7 -> i32 7, 2+7 = 9. The - * f64 element rides its eightbyte in AX at receive and is re-read - * from X0 at field-read; the i64 (t.1) rides DX — the dropped - * word. */ - { "f64_i64", - "package main;\n" - "fn mk() (f64, i64) = { return (2.5, 7); };\n" - "export fn main() i32 = {\n" - "\tlet t = mk();\n" - "\tlet f: f64 = t.0;\n" - "\tlet i: i64 = t.1;\n" - "\treturn (f: i32) + (i: i32);\n" - "};\n", 9 }, - /* order-swap (i64, f64): f64 in the 2nd slot. i=7, f=2.5 -> 2, - * 7+2 = 9. Confirms the spill is position-agnostic (both words go - * over the integer cursor regardless of which holds the f64). */ - { "i64_f64", - "package main;\n" - "fn mk() (i64, f64) = { return (7, 2.5); };\n" - "export fn main() i32 = {\n" - "\tlet t = mk();\n" - "\tlet i: i64 = t.0;\n" - "\tlet f: f64 = t.1;\n" - "\treturn (i: i32) + (f: i32);\n" - "};\n", 9 }, - /* all-integer (i64, i64): a=5, b=7, 5+7 = 12. NOT a no-op control - * — pre-fix wwstage dropped DX here too (proves the fix is not - * f64-gated). */ - { "i64_i64", - "package main;\n" - "fn mk() (i64, i64) = { return (5, 7); };\n" - "export fn main() i32 = {\n" - "\tlet t = mk();\n" - "\tlet a: i64 = t.0;\n" - "\tlet b: i64 = t.1;\n" - "\treturn (a: i32) + (b: i32);\n" - "};\n", 12 }, - /* CONTROL — destructure form `let (f,i) = mk()` (cgmlet + tupstore - * cursor, a different and already-correct path; bootstrap relies on - * it, which is why 990-997 pass). The fix is in cglet/N_LET only, so - * this stays byte-id pre- and post-fix. f=2.5->2, i=7, 2+7 = 9. */ - { "ctl_destr", - "package main;\n" - "fn mk() (f64, i64) = { return (2.5, 7); };\n" - "export fn main() i32 = {\n" - "\tlet (f, i) = mk();\n" - "\treturn (f: i32) + (i: i32);\n" - "};\n", 9, 1 }, - /* CONTROL — wide/str element single-var (i64, str) is a 32B tuple - * handled by the separate 32B receive branch; the fix's sz==16 gate - * excludes it, so it stays byte-id pre- and post-fix. t.0 = 7. */ - { "ctl_str", - "package main;\n" - "fn mk() (i64, str) = { return (7, \"hi\"); };\n" - "export fn main() i32 = {\n" - "\tlet t: (i64, str) = mk();\n" - "\treturn t.0: i32;\n" - "};\n", 7 }, - /* CONTROL — over-catch guard. A 16B struct{i64,i64} from-call receive - * is sz==16 like the bug rows, but rettupleof returns nil for a - * non-tuple return, so the fix's new branch must NOT hijack it; it - * stays on the generic struct store and byte-id pre- and post-fix. - * Pins that the fix's tuple/struct discrimination (#102) holds. a=5, - * b=7, 5+7 = 12. */ - { "ctl_struct", - "package main;\n" - "type pair = struct { a: i64, b: i64 };\n" - "fn mk() pair = { return pair { a = 5, b = 7 }; };\n" - "export fn main() i32 = {\n" - "\tlet t = mk();\n" - "\treturn (t.a: i32) + (t.b: i32);\n" - "};\n", 12 }, - { 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; -} +/* the ctl_destr construct: an un-annotated float-destructure binding. */ +static const char *src = + "package main;\n" + "fn mk() (f64, i64) = { return (2.5, 7); };\n" + "export fn main() i32 = {\n" + "\tlet (f, i) = mk();\n" + "\treturn (f: i32) + (i: i32);\n" + "};\n"; int main(void) @@ -162,116 +53,44 @@ main(void) bin = absbin; } - char w6c[1100], w6c_ww[1100]; - snprintf(w6c, sizeof w6c, "%s/w6c", bin); + char w6c_ww[1100]; snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin); if (access(w6c_ww, X_OK) != 0) { fprintf(stderr, "tuprecv: w6c_ww missing — cannot run the " - "cs==ww byte-id gate (the whole point of this test)\n"); + "asserttyped-absence pin\n"); return 1; } - int n = 0, fail = 0; - for (int i = 0; rows[i].src; i++, n++) { - /* src reused across the build-phase (a) AND the w6c/w6c_ww - * byte-id phase (b)/(c); keep src, outbin and both .s under one - * tmpdir and defer a single rm -rf to the end so the compiler's - * .sepwork scratch (derived from the source path) lands in - * tmpdir, not bare /tmp. */ - char tmpdir[64], rmcmd[160]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/wwtup_%d_d_%d", - getpid(), i); - mkdir(tmpdir, 0755); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + char tmpdir[64], rmcmd[160], srcp[128], errf[128], cmd[2048]; + snprintf(tmpdir, sizeof tmpdir, "/tmp/wwtuprecv_%d", getpid()); + mkdir(tmpdir, 0755); + snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); + snprintf(srcp, sizeof srcp, "%s/ctl_destr.ww", tmpdir); + snprintf(errf, sizeof errf, "%s/err.txt", tmpdir); - char src[128]; - snprintf(src, sizeof src, "%s/wwtup_%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); + FILE *f = fopen(srcp, "wb"); + if (f == NULL) { runwait(rmcmd); return 1; } + fputs(src, f); + fclose(f); - /* (a) cstage build + run; -o pins the binary into tmpdir. */ - char outbin[128]; - snprintf(outbin, sizeof outbin, "%s/wwtup_%d_%d", - tmpdir, getpid(), i); + int fail = 0; - 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++; - } - - /* (b) cs==ww byte-id gate: emit .s from both stages, cmp. - * FAILS on the pre-fix wwstage divergence (dropped MOVQ DX). */ - char cs_s[128], ws_s[128]; - snprintf(cs_s, sizeof cs_s, "%s/wwtup_%d_%d_cs.s", - tmpdir, getpid(), i); - snprintf(ws_s, sizeof ws_s, "%s/wwtup_%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++; - } - - /* (c) #121 A-narrow stamp gate: the un-annotated float- - * destructure binding must now carry a checker type stamp, so - * w6c_ww emits no `asserttyped:` diagnostic. Non-vacuous — - * pre-stamp (HEAD) w6c_ww fires asserttyped on the f64 binding - * ident (nil n.type_). */ - if (rows[i].chk_stamped) { - char errf[128]; - snprintf(errf, sizeof errf, - "%s/wwtup_%d_%d_err.txt", tmpdir, getpid(), i); - snprintf(cmd, sizeof cmd, - "%s -o /dev/null %s 2>%s", w6c_ww, src, errf); - runwait(cmd); - snprintf(cmd, sizeof cmd, - "grep -q asserttyped %s", errf); - if (runwait(cmd) == 0) { - fprintf(stderr, "row[%s]: w6c_ww emitted " - "asserttyped (destructure binding " - "unstamped)\n", rows[i].label); - fail++; - } - } - runwait(rmcmd); + /* w6c_ww must compile the construct cleanly AND emit no asserttyped. */ + snprintf(cmd, sizeof cmd, "%s -o /dev/null %s 2>%s", w6c_ww, srcp, errf); + if (runwait(cmd) != 0) { + fprintf(stderr, "tuprecv: w6c_ww failed to compile the " + "float-destructure construct\n"); + fail++; + } + snprintf(cmd, sizeof cmd, "grep -q asserttyped %s", errf); + if (runwait(cmd) == 0) { + fprintf(stderr, "tuprecv: w6c_ww emitted asserttyped on the " + "un-annotated float destructure binding (#121 stamp net)\n"); + fail++; } - if (fail) { - fprintf(stderr, "%d/%d tuple-receive tests failed\n", - fail, n); - return 1; - } - printf("tuprecv: %d/%d ok (cstage run + cs==ww byte-id)\n", n, n); + runwait(rmcmd); + if (fail) return 1; + printf("tuprecv: asserttyped-absence pin ok (#121)\n"); return 0; } diff --git a/test/wcc/989_tagtupfieldsize_run.c b/test/wcc/989_tagtupfieldsize_run.c deleted file mode 100644 index eaea0eb5..00000000 --- a/test/wcc/989_tagtupfieldsize_run.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * 989_tagtupfieldsize_run (#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. - * - * THE BUG (cat-A silent miscompile, gate-blind): wwstage paramfieldsize - * (selfhost/cmd/wcc/cgenstmt.ww) — the structural sizer the for-range - * destructure uses for the tuple stride and each field's offset — had no - * tagged-union arm, so a `(i64|bool)` field sized 8 (the default) instead - * of its 16B box (tag + slot-padded payload). The for-range DESTRUCTURE-READ - * path is unguarded by the #270-1c array-literal construction gate (#43 - * lineage; tuples built by whole-tuple element store). The 8B bind copied - * only the tag word and read the payload from stale stack: cs=56 (correct, - * cstage's tp->type->size=16) vs ww=9 — the cat-A signature. THE FIX: read - * the tagged box width from the checker-stamped tinfo (rule-13), mirroring - * the N_TSLICE arm's type-table SSoT; 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, cgenexpr #22a); a cast source loud-rejects. - * - * Rows (cstage `ww` + wwstage `ww_ww`; rule-10; pin the absolute value): - * row | reads | exit (cs==ww) - * ------------------+--------------------------------------+-------------- - * tag_payload | match(a){i64=>v}+n, payloads 7/9 | 56 (7+10+9+30) - * scalar_tuple_ctl | for(.. [2](i64,i64)) — arm untouched | 48 (control) - * Pre-fix (8B bind, payload word dropped): tag_payload ww=9 != cs=56. - */ -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { - const char *label; - const char *src; - int want_exit; /* >= 0: also pin the absolute value; -1: cs==ww only */ -}; - -static const struct row rows[] = { - /* (1) tag_payload (#53 teeth): the tagged binding's payload word must - * be copied. Pre-fix the 8B bind dropped it → match reads stale → 9. */ - { "tag_payload", - "package main;\n" - "type tu = (i64 | bool);\n" - "export fn main() int = {\n" - " let t0: tu = 7: tu;\n" - " let t1: tu = 9: tu;\n" - " let xs: [2](tu, i64);\n" - " xs[0] = (t0, 10);\n" - " xs[1] = (t1, 30);\n" - " let sum: i64 = 0;\n" - " for (let (a, n) .. xs) {\n" - " match (a) {\n" - " case let v: i64 => sum = sum + v + n;\n" - " case let b: bool => sum = sum + 1000;\n" - " };\n" - " };\n" - " return sum: int;\n" - "};\n", - 56 }, - - /* (2) control — scalar-only tuple (no tagged field): paramfieldsize - * already handled it; the new tagged arm must not touch it. 3+10+5+30. */ - { "scalar_tuple_ctl", - "package main;\n" - "export fn main() int = {\n" - " let xs: [2](i64, i64);\n" - " xs[0] = (3, 10); xs[1] = (5, 30);\n" - " let sum: i64 = 0;\n" - " for (let (a, b) .. xs) { sum = sum + a + b; };\n" - " return sum: int;\n" - "};\n", - 48 }, -}; - -/* run_build — build+run `src` via `driver`; returns the binary's exit - * code, or -1 on a build failure. */ -static int -run_build(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/tagtupfs_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tagtupfs_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tagtupfs_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -2; } - fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", - driver, outbin, src); - int brc = runwait(cmd); - - int got = -1; - if (brc == 0) got = runwait(outbin); - - runwait(rmcmd); - return brc == 0 ? got : -1; -} - -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[1024], wdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - int have_ww = (access(wdrv, X_OK) == 0); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int i = 0; i < n; i++) { - total++; - int gc = run_build(cdrv, &rows[i], i); - if (gc < 0) { - fprintf(stderr, "tagtupfieldsize_run[cstage][%s]: build/run " - "failed (got %d)\n", rows[i].label, gc); - fail++; - continue; - } - if (rows[i].want_exit >= 0 && gc != rows[i].want_exit) { - fprintf(stderr, "tagtupfieldsize_run[cstage][%s]: exit=%d " - "want=%d\n", rows[i].label, gc, rows[i].want_exit); - fail++; - } - if (!have_ww) { - fprintf(stderr, "tagtupfieldsize_run: skip wwstage (no %s)\n", - wdrv); - continue; - } - int gw = run_build(wdrv, &rows[i], i); - /* rule-10: the cat-A invariant is cs == ww */ - if (gw != gc) { - fprintf(stderr, "tagtupfieldsize_run[%s]: cs=%d != ww=%d " - "(tagged tuple-field stride/offset divergence — #53)\n", - rows[i].label, gc, gw); - fail++; - } - if (rows[i].want_exit >= 0 && gw != rows[i].want_exit) { - fprintf(stderr, "tagtupfieldsize_run[wwstage][%s]: exit=%d " - "want=%d\n", rows[i].label, gw, rows[i].want_exit); - fail++; - } - } - - if (fail) { - fprintf(stderr, "tagtupfieldsize_run: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("tagtupfieldsize_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/989_tupfieldsize_run.c b/test/wcc/989_tupfieldsize_run.c deleted file mode 100644 index bafc0f96..00000000 --- a/test/wcc/989_tupfieldsize_run.c +++ /dev/null @@ -1,226 +0,0 @@ -/* - * 989_tupfieldsize_run — F7-c4 (#43): a for-range destructure over an - * array of tuples must stride by the tuple's TRUE size; a slice/str/nested - * tuple FIELD carries its full width, not the 8B scalar default. - * - * THE BUG (cat-A silent miscompile, gate-blind): paramfieldsize - * (selfhost/cmd/wcc/cgenstmt.ww) — the structural sizer the for-range - * destructure uses to compute the tuple stride and each field's offset — - * had no N_TSLICE / N_TTUPLE arm, so a `[]T` tuple-field sized 8 (the - * default) instead of its 24B header. The #270-1c array-literal guard - * blocks only the literal CONSTRUCTION; the for-range DESTRUCTURE-READ - * path is unguarded (ken's oracle refuted "unreachable"). For - * `[2]([]u8, i64)` the wwstage strode the tuple at 16 / read field-2 at - * offset 8, vs cstage's 32 / 24 — a cs≠ww divergence (the cat-A - * signature). cstage's tp->type->size (cmd/w6c/cgen.c N_FORRANGE) reads - * the true width. THE FIX: add the N_TSLICE arm (tyslicesize() = 24, the - * slice-header SSoT, rule-13) and the N_TTUPLE arm (sum of 8B-floored - * element slots, recursive), aligning wwstage UP. - * - * #40 (#263, now CLOSED both stages — fused): F7-c4 fixed only the STRIDE, - * so the slice-field row was pinned cs==ww (MATCH-ONLY) at 40, NOT the - * arithmetic-expected 45 — because the destructure LOAD-into-binding copied - * only the PTR word of the 24B slice binding, dropping .len/.cap (both stages - * identically; nobody had re-measured the ww absolute after F7-c4). The fused - * #40 fix (cgen.c N_FORRANGE destructure + cgenstmt.ww cgforrange twin: copy - * the binding's FULL extent for an aggregate sz>8 binding, same word-run + - * sized-tail idiom as the non-destructure copy) lands all three header words. - * The rows now pin the ABSOLUTE value cs==ww across all three words — the - * recurring dropped word in this 24B-aggregate-destructure family is the CAP - * (cf #29 chained-dot, #43 sizer), so the cap row reads b.cap with cap!=len - * (sliced) to teeth it; a ptr+len-only set would pass green while cap stayed - * stale (exactly how #40 hid). - * - * Tuples are built by whole-tuple element store (`xs[i] = (..)`). - * - * Rows (cstage `ww` + wwstage `ww_ww`; rule-10; all pin the absolute value): - * row | reads | exit (cs==ww) - * ------------------+------------------------------------+-------------- - * len_read | len(b)+n over [2]([]u8,i64) | 45 (2+10+3+30) - * cap_read | b.cap+n, sliced caps 4/5 (cap!=len) | 49 (4+10+5+30) - * ptr_read | b[0]+ (byte through ptr) | 217 (97+120) - * scalar_tuple_ctl | for(.. [2](i64,i64)) | 48 (control) - * Pre-fix (single-word copy): len_read=40, cap_read=40 (len/cap words both 0), - * ptr_read=217 (word0 always copied — coverage, no teeth); cs==ww==wrong (the - * #263 both-wrong-identical residual the fused fix retires). - */ -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { - const char *label; - const char *src; - int want_exit; /* >= 0: also pin the absolute value; -1: cs==ww only */ -}; - -static const struct row rows[] = { - /* (1) len_read (#40 teeth, word 1): the slice binding's .len must be - * copied. Pre-fix the single-word copy dropped it → len(b)=0 → 40. */ - { "len_read", - "package main;\n" - "export fn main() int = {\n" - " let b0: []u8 = ['a', 'b'];\n" - " let b1: []u8 = ['x', 'y', 'z'];\n" - " let xs: [2]([]u8, i64);\n" - " xs[0] = (b0, 10);\n" - " xs[1] = (b1, 30);\n" - " let sum: i64 = 0;\n" - " for (let (b, n) .. xs) {\n" - " sum = sum + len(b): i64 + n;\n" - " };\n" - " return sum: int;\n" - "};\n", - 45 }, - - /* (2) cap_read (#40 teeth, word 2 — THE recurring dropped word): caps - * 4/5 are SLICED so cap != len (2/3); pre-fix b.cap=0 → 40. */ - { "cap_read", - "package main;\n" - "export fn main() int = {\n" - " let base0: []u8 = ['a', 'b', 'c', 'd'];\n" - " let base1: []u8 = ['p', 'q', 'r', 's', 't'];\n" - " let xs: [2]([]u8, i64);\n" - " xs[0] = (base0[0:2], 10);\n" - " xs[1] = (base1[0:3], 30);\n" - " let sum: i64 = 0;\n" - " for (let (b, n) .. xs) {\n" - " sum = sum + b.cap: i64 + n;\n" - " };\n" - " return sum: int;\n" - "};\n", - 49 }, - - /* (3) ptr_read (word 0 — coverage; word0 was always copied, no teeth): - * a byte read through the binding's .ptr. b0[0]='a'=97, b1[0]='x'=120. */ - { "ptr_read", - "package main;\n" - "export fn main() int = {\n" - " let b0: []u8 = ['a', 'b'];\n" - " let b1: []u8 = ['x', 'y', 'z'];\n" - " let xs: [2]([]u8, i64);\n" - " xs[0] = (b0, 10);\n" - " xs[1] = (b1, 30);\n" - " let sum: i64 = 0;\n" - " for (let (b, n) .. xs) { sum = sum + b[0]: i64; };\n" - " return sum: int;\n" - "};\n", - 217 }, - - /* (4) control — scalar-only tuple (no aggregate field): paramfieldsize - * already handled it; the sz>8 arm must not touch it. 3+10+5+30 == 48. */ - { "scalar_tuple_ctl", - "package main;\n" - "export fn main() int = {\n" - " let xs: [2](i64, i64);\n" - " xs[0] = (3, 10); xs[1] = (5, 30);\n" - " let sum: i64 = 0;\n" - " for (let (a, b) .. xs) { sum = sum + a + b; };\n" - " return sum: int;\n" - "};\n", - 48 }, -}; - -/* run_build — build+run `src` via `driver`; returns the binary's exit - * code, or -1 on a build failure. */ -static int -run_build(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/tupfs_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tupfs_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tupfs_%d_%d", tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return -2; } - fputs(r->src, f); - fclose(f); - - snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null", - driver, outbin, src); - int brc = runwait(cmd); - - int got = -1; - if (brc == 0) got = runwait(outbin); - - runwait(rmcmd); - return brc == 0 ? got : -1; -} - -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[1024], wdrv[1024]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - int have_ww = (access(wdrv, X_OK) == 0); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int i = 0; i < n; i++) { - total++; - int gc = run_build(cdrv, &rows[i], i); - /* cstage must build+run */ - if (gc < 0) { - fprintf(stderr, "tupfieldsize_run[cstage][%s]: build/run " - "failed (got %d)\n", rows[i].label, gc); - fail++; - continue; - } - if (rows[i].want_exit >= 0 && gc != rows[i].want_exit) { - fprintf(stderr, "tupfieldsize_run[cstage][%s]: exit=%d " - "want=%d\n", rows[i].label, gc, rows[i].want_exit); - fail++; - } - if (!have_ww) { - fprintf(stderr, "tupfieldsize_run: skip wwstage (no %s)\n", - wdrv); - continue; - } - int gw = run_build(wdrv, &rows[i], i); - /* rule-10: the cat-A invariant is cs == ww */ - if (gw != gc) { - fprintf(stderr, "tupfieldsize_run[%s]: cs=%d != ww=%d " - "(stride/offset divergence — #43)\n", - rows[i].label, gc, gw); - fail++; - } - if (rows[i].want_exit >= 0 && gw != rows[i].want_exit) { - fprintf(stderr, "tupfieldsize_run[wwstage][%s]: exit=%d " - "want=%d\n", rows[i].label, gw, rows[i].want_exit); - fail++; - } - } - - if (fail) { - fprintf(stderr, "tupfieldsize_run: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("tupfieldsize_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/data/overcap_chained_index_be/case.ww b/test/wcc/data/overcap_chained_index_be/case.ww new file mode 100644 index 00000000..ff0f5c2a --- /dev/null +++ b/test/wcc/data/overcap_chained_index_be/case.ww @@ -0,0 +1,11 @@ +//ww:error "#234-tail" +package main; +fn wide() ([]u8, []u8) = { + let a: []u8; a.len = 1; let b: []u8; b.len = 2; + return (a, b); +}; +export fn main() i32 = { + let a: [2][2]([]u8, []u8); + a[0][1] = wide(); + return 0; +}; diff --git a/test/wcc/data/overcap_global_be/case.ww b/test/wcc/data/overcap_global_be/case.ww new file mode 100644 index 00000000..023da5da --- /dev/null +++ b/test/wcc/data/overcap_global_be/case.ww @@ -0,0 +1,12 @@ +//ww:error "#234-tail" +package main; +fn wide() ([]u8, []u8) = { + let a: []u8; a.len = 1; let b: []u8; b.len = 2; + return (a, b); +}; +type S = struct { f: ([]u8, []u8) }; +let g: S; +export fn main() i32 = { + g.f = wide(); + return 0; +}; diff --git a/test/wcc/data/overcap_ndot_index_be/case.ww b/test/wcc/data/overcap_ndot_index_be/case.ww new file mode 100644 index 00000000..d129ddb6 --- /dev/null +++ b/test/wcc/data/overcap_ndot_index_be/case.ww @@ -0,0 +1,12 @@ +//ww:error "#234-tail" +package main; +fn wide() ([]u8, []u8) = { + let a: []u8; a.len = 1; let b: []u8; b.len = 2; + return (a, b); +}; +type S = struct { arr: [2]([]u8, []u8) }; +export fn main() i32 = { + let s: S; + s.arr[1] = wide(); + return 0; +}; diff --git a/test/wcc/data/overcap_rtindex_be/case.ww b/test/wcc/data/overcap_rtindex_be/case.ww new file mode 100644 index 00000000..5aa3e714 --- /dev/null +++ b/test/wcc/data/overcap_rtindex_be/case.ww @@ -0,0 +1,12 @@ +//ww:error "#234-tail" +package main; +fn wide() ([]u8, []u8) = { + let a: []u8; a.len = 1; let b: []u8; b.len = 2; + return (a, b); +}; +fn ix() int = { return 0; }; +export fn main() i32 = { + let arr: [2]([]u8, []u8); + arr[ix()] = wide(); + return 0; +}; diff --git a/test/wcc/data/overcap_via_ptr_be/case.ww b/test/wcc/data/overcap_via_ptr_be/case.ww new file mode 100644 index 00000000..62c9e0ca --- /dev/null +++ b/test/wcc/data/overcap_via_ptr_be/case.ww @@ -0,0 +1,13 @@ +//ww:error "#234-tail" +package main; +fn wide() ([]u8, []u8) = { + let a: []u8; a.len = 1; let b: []u8; b.len = 2; + return (a, b); +}; +type S = struct { f: ([]u8, []u8) }; +export fn main() i32 = { + let s: S; + let p: *S = &s; + p.f = wide(); + return 0; +}; diff --git a/test/wcc/data/tldb_nested_tuple_arg/case.ww b/test/wcc/data/tldb_nested_tuple_arg/case.ww new file mode 100644 index 00000000..57757e1c --- /dev/null +++ b/test/wcc/data/tldb_nested_tuple_arg/case.ww @@ -0,0 +1,15 @@ +//ww:error "tuple element must be a scalar, str, slice, or tagged-union" +// Lifted from test/wcc/945_tuple_lit_declblind_run.c reject row +// "nested_tuple_arg" (#64/#68/rule-7): a NESTED-TUPLE tuple-arg element has +// no transport and stays rule-7 LOUD. The #64/#68 fix graduates ONLY a +// declared-tagged element; the TY_TUPLE/STRUCT/ARRAY arms are preserved, so a +// future widen-everything regression re-breaks this. The substring is the +// shared diagnostic body, byte-identical on both stages (no file:line, no +// cstage "ww: " prefix). +package main; +fn h(t: ((i32, i32), i64)) i32 = { + return 0; +}; +export fn main() i32 = { + return h(((1, 2), 3)); +}; diff --git a/test/wcc/data/tslot_b22_reject_let_void_slip/case.ww b/test/wcc/data/tslot_b22_reject_let_void_slip/case.ww new file mode 100644 index 00000000..cfd0c56b --- /dev/null +++ b/test/wcc/data/tslot_b22_reject_let_void_slip/case.ww @@ -0,0 +1,10 @@ +//ww:error "over-cap tuple initialiser from a non-call source unwired" +package main; +fn pr() ((void | size), (void | size), size) = { + let t: ((void | size), (void | size), size) = (void, void, 1); + return t; +}; +export fn main() i32 = { + let t = pr(); + return t.2: i32; +}; diff --git a/test/wcc/data/tslot_b22_reject_nonident_source/case.ww b/test/wcc/data/tslot_b22_reject_nonident_source/case.ww new file mode 100644 index 00000000..84ebca28 --- /dev/null +++ b/test/wcc/data/tslot_b22_reject_nonident_source/case.ww @@ -0,0 +1,11 @@ +//ww:error "#22b: tagged element in an over-cap (sret) tuple return from a non-ident or widening source unwired" +package main; +fn g() (void | size) = { return 5: size; }; +fn pr() ((void | size), (void | size), size) = { + let mx: (void | size) = 2: size; + return (g(), mx, 7); +}; +export fn main() i32 = { + let t = pr(); + return t.2: i32; +}; diff --git a/test/wcc/data/tslot_b22_reject_widen_skew/case.ww b/test/wcc/data/tslot_b22_reject_widen_skew/case.ww new file mode 100644 index 00000000..05511f2f --- /dev/null +++ b/test/wcc/data/tslot_b22_reject_widen_skew/case.ww @@ -0,0 +1,10 @@ +//ww:error "#22b: tagged element in an over-cap (sret) tuple return from a non-ident or widening source unwired" +package main; +fn pr() ((void | size), (void | size), size) = { + let mx: (void | size) = 5: size; + return (1: size, mx, 9); +}; +export fn main() i32 = { + let t = pr(); + return t.2: i32; +}; diff --git a/test/wcc/data/tslot_charset_slice_append_reject/case.ww b/test/wcc/data/tslot_charset_slice_append_reject/case.ww new file mode 100644 index 00000000..b27ee993 --- /dev/null +++ b/test/wcc/data/tslot_charset_slice_append_reject/case.ww @@ -0,0 +1,8 @@ +//ww:error "element kind unsupported" +package main; +export fn main() i32 = { + let s: [](u32, u32) = []; + append(s, (1, 2)); + if (len(s) != 1) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_fc_loud_cast_third_union/case.ww b/test/wcc/data/tslot_fc_loud_cast_third_union/case.ww new file mode 100644 index 00000000..b6e051e8 --- /dev/null +++ b/test/wcc/data/tslot_fc_loud_cast_third_union/case.ww @@ -0,0 +1,9 @@ +//ww:error "tagged cast source shape unwired" +package main; +type un3 = (void | size | str); +type un4 = (void | size | str | *u8); +export fn main() i32 = { + let w: un4 = (5: size): un3; + if (!(w is size)) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_fc_loud_global_unwrap/case.ww b/test/wcc/data/tslot_fc_loud_global_unwrap/case.ww new file mode 100644 index 00000000..879d1ae7 --- /dev/null +++ b/test/wcc/data/tslot_fc_loud_global_unwrap/case.ww @@ -0,0 +1,15 @@ +//ww:error "on a global tagged ident unwired" +package main; +type err = !void; +let g: (size | err) = 7: size; +fn use() (size | err) = { + let x = g?; + return x: size; +}; +export fn main() i32 = { + match (use()) { + case let s: size => { if (s != 7) { return 1; }; }; + case err => { return 2; }; + }; + return 0; +}; diff --git a/test/wcc/data/tslot_t22_reject_arg/case.ww b/test/wcc/data/tslot_t22_reject_arg/case.ww new file mode 100644 index 00000000..95516b6b --- /dev/null +++ b/test/wcc/data/tslot_t22_reject_arg/case.ww @@ -0,0 +1,8 @@ +//ww:error "tuple arg element kind unsupported" +package main; +fn send(t: ((void | size), size)) size = { return t.1; }; +export fn main() i32 = { + let mn: (void | size) = 5: size; + let t: ((void | size), size) = (mn, 4); + return send(t): i32; +}; diff --git a/test/wcc/data/tslot_t22_reject_elem_write/case.ww b/test/wcc/data/tslot_t22_reject_elem_write/case.ww new file mode 100644 index 00000000..a59eabd8 --- /dev/null +++ b/test/wcc/data/tslot_t22_reject_elem_write/case.ww @@ -0,0 +1,8 @@ +//ww:error "unsupported assign target shape" +package main; +export fn main() i32 = { + let mn: (void | size) = 5: size; + let t: ((void | size), size) = (mn, 4); + t.1 = 9; + return t.1: i32; +}; diff --git a/test/wcc/data/tslot_t22_reject_global_init/case.ww b/test/wcc/data/tslot_t22_reject_global_init/case.ww new file mode 100644 index 00000000..d3affccf --- /dev/null +++ b/test/wcc/data/tslot_t22_reject_global_init/case.ww @@ -0,0 +1,4 @@ +//ww:error "unsupported element init (int/str literals only; rule 7)" +package main; +let g: ((void | size), i64) = (5, 4); +export fn main() i32 = { return g.1: i32; }; diff --git a/test/wcc/data/tslot_t22_reject_lit_call_elem/case.ww b/test/wcc/data/tslot_t22_reject_lit_call_elem/case.ww new file mode 100644 index 00000000..a5bfab83 --- /dev/null +++ b/test/wcc/data/tslot_t22_reject_lit_call_elem/case.ww @@ -0,0 +1,8 @@ +//ww:error "tagged tuple element from a non-local source shape unwired" +package main; +fn g() (void | size) = { return 5: size; }; +fn mk() ((void | size), size) = { return (g(), 4); }; +export fn main() i32 = { + let t = mk(); + return t.1: i32; +}; diff --git a/test/wcc/data/tslot_t22_reject_overcap_lit_strs/case.ww b/test/wcc/data/tslot_t22_reject_overcap_lit_strs/case.ww new file mode 100644 index 00000000..ad1bcfb5 --- /dev/null +++ b/test/wcc/data/tslot_t22_reject_overcap_lit_strs/case.ww @@ -0,0 +1,7 @@ +//ww:error "tuple literal exceeds register-return ABI capacity" +package main; +export fn main() i32 = { + let t: (str, str) = ("ab", "cde"); + if (t.0.len != 2) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_t22_reject_overcap_lit_tagged/case.ww b/test/wcc/data/tslot_t22_reject_overcap_lit_tagged/case.ww new file mode 100644 index 00000000..0aaefa06 --- /dev/null +++ b/test/wcc/data/tslot_t22_reject_overcap_lit_tagged/case.ww @@ -0,0 +1,8 @@ +//ww:error "tuple literal exceeds register-return ABI capacity" +package main; +export fn main() i32 = { + let e: (void | u32 | str) = "abc"; + let t: (u64, (void | u32 | str)) = (7u64, e); + if (t.0 != 7u64) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_t2_reject_chain_arg/case.ww b/test/wcc/data/tslot_t2_reject_chain_arg/case.ww new file mode 100644 index 00000000..3498dfe3 --- /dev/null +++ b/test/wcc/data/tslot_t2_reject_chain_arg/case.ww @@ -0,0 +1,12 @@ +//ww:error "tuple arg from unsupported source shape" +package main; +type holder = struct { t: (i64, i64) }; +fn sum(t: (i64, i64)) i64 = { + return t.0 + t.1; +}; +export fn main() i32 = { + let tt: (i64, i64) = (1, 2); + let h: holder = holder{ t = tt }; + if (sum(h.t) != 3) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_t2_reject_nested_elem_arg/case.ww b/test/wcc/data/tslot_t2_reject_nested_elem_arg/case.ww new file mode 100644 index 00000000..53631a8b --- /dev/null +++ b/test/wcc/data/tslot_t2_reject_nested_elem_arg/case.ww @@ -0,0 +1,11 @@ +//ww:error "tuple element must be a scalar" +package main; +fn f(t: ((i64, i64), i64)) i64 = { + return t.1; +}; +export fn main() i32 = { + let inner: (i64, i64) = (1, 2); + let t: ((i64, i64), i64) = (inner, 9); + if (f(t) != 9) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_t2_reject_overcap_ident_arg/case.ww b/test/wcc/data/tslot_t2_reject_overcap_ident_arg/case.ww new file mode 100644 index 00000000..e141fd0f --- /dev/null +++ b/test/wcc/data/tslot_t2_reject_overcap_ident_arg/case.ww @@ -0,0 +1,13 @@ +//ww:error "tuple ident exceeds register-return ABI capacity" +package main; +fn f(t: (str, str)) i64 = { + return (len(t.0) + len(t.1)): i64; +}; +fn g() (str, str) = { + return ("ab", "cde"); +}; +export fn main() i32 = { + let t: (str, str) = g(); + if (f(t) != 5) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_t3_reject_float_global/case.ww b/test/wcc/data/tslot_t3_reject_float_global/case.ww new file mode 100644 index 00000000..dff74b3e --- /dev/null +++ b/test/wcc/data/tslot_t3_reject_float_global/case.ww @@ -0,0 +1,7 @@ +//ww:error "unsupported element init (int/str literals only; rule 7)" +package main; +let g: (f64, i64) = (2.5, 4); +export fn main() i32 = { + if (g.1 != 4) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_t3_reject_global_write/case.ww b/test/wcc/data/tslot_t3_reject_global_write/case.ww new file mode 100644 index 00000000..83eb0f65 --- /dev/null +++ b/test/wcc/data/tslot_t3_reject_global_write/case.ww @@ -0,0 +1,7 @@ +//ww:error "unsupported assign target shape" +package main; +let g: (i64, i64) = (3, 4); +export fn main() i32 = { + g.0 = 7; + return 0; +}; diff --git a/test/wcc/data/tslot_t3_reject_whole_value/case.ww b/test/wcc/data/tslot_t3_reject_whole_value/case.ww new file mode 100644 index 00000000..c6dddee6 --- /dev/null +++ b/test/wcc/data/tslot_t3_reject_whole_value/case.ww @@ -0,0 +1,8 @@ +//ww:error "global tuple as a first-class value unwired" +package main; +let g: (i64, i64) = (3, 4); +export fn main() i32 = { + let q: (i64, i64) = g; + if (q.0 != 3) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_t44_inferred_nested/case.ww b/test/wcc/data/tslot_t44_inferred_nested/case.ww new file mode 100644 index 00000000..95c93e80 --- /dev/null +++ b/test/wcc/data/tslot_t44_inferred_nested/case.ww @@ -0,0 +1,7 @@ +//ww:error "tuple element must be a scalar" +package main; +export fn main() i32 = { + let t = ((4, 2), 36); + if (t.1 != 36) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_t57_loud_call_elem/case.ww b/test/wcc/data/tslot_t57_loud_call_elem/case.ww new file mode 100644 index 00000000..9e255e77 --- /dev/null +++ b/test/wcc/data/tslot_t57_loud_call_elem/case.ww @@ -0,0 +1,11 @@ +//ww:error "tagged tuple element from a non-local source shape unwired" +package main; +type un16 = (void | size); +fn mke() un16 = { + return 5: size; +}; +export fn main() i32 = { + let t: (un16, size) = (mke(), 9); + if (t.1 != 9) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_t57_loud_overcap_boundary/case.ww b/test/wcc/data/tslot_t57_loud_overcap_boundary/case.ww new file mode 100644 index 00000000..765ff4cc --- /dev/null +++ b/test/wcc/data/tslot_t57_loud_overcap_boundary/case.ww @@ -0,0 +1,11 @@ +//ww:error "tagged element in an over-cap (sret) tuple return" +package main; +type un = (void | size); +fn mk() (un, un, size, size) = { + return (5: size, void, 8, 9); +}; +export fn main() i32 = { + let t = mk(); + if (t.2 != 8) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tslot_t57_loud_remap/case.ww b/test/wcc/data/tslot_t57_loud_remap/case.ww new file mode 100644 index 00000000..4dd10bb9 --- /dev/null +++ b/test/wcc/data/tslot_t57_loud_remap/case.ww @@ -0,0 +1,14 @@ +//ww:error "widening into a wider declared union slot needs a tag remap" +package main; +type st16 = struct { a: size, b: size }; +type small = (void | u8); +type big = (void | u8 | st16); +fn mk() (big, size) = { + let s: small = 5u8; + return (s, 9); +}; +export fn main() i32 = { + let t = mk(); + if (t.1 != 9) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/tupidx_forrange_global_loud/case.ww b/test/wcc/data/tupidx_forrange_global_loud/case.ww new file mode 100644 index 00000000..b9432ec2 --- /dev/null +++ b/test/wcc/data/tupidx_forrange_global_loud/case.ww @@ -0,0 +1,9 @@ +//ww:error "for-range over a module-global" +package main; +fn fa(c: rune) bool = { return c == 'a'; }; +const tbl: [](str, *fn(c: rune) bool) = [("ab", &fa), ("cdef", &fa)]; +export fn main() i32 = { + let n: int = 0; + for (let e .. tbl) { n += 1; }; + return n: i32; +}; diff --git a/test/wcc/data/tupparam_gp_overflow_loudstop/case.ww b/test/wcc/data/tupparam_gp_overflow_loudstop/case.ww new file mode 100644 index 00000000..e1c22e13 --- /dev/null +++ b/test/wcc/data/tupparam_gp_overflow_loudstop/case.ww @@ -0,0 +1,9 @@ +//ww:error "tuple param element overflows integer arg regs (DI/SI/DX/CX/R8/R9); stitch out of scope, see #163" +package main; +fn pair(a: i64, b: i64) (i64, i64) = { return (a, b); }; +fn f(a: i64, b: i64, c: i64, d: i64, e: i64, t: (i64, i64)) i64 = { + return a + b + c + d + e + t.0 + t.1; +}; +export fn main() i32 = { + return (f(1, 2, 3, 4, 5, pair(6, 7)): i32); +}; diff --git a/test/wcc/data/tupunion_bool_literal/case.ww b/test/wcc/data/tupunion_bool_literal/case.ww new file mode 100644 index 00000000..08e76ad2 --- /dev/null +++ b/test/wcc/data/tupunion_bool_literal/case.ww @@ -0,0 +1,4 @@ +//ww:error "variant tag unresolved (untyped/literal tuple element" +package main; +fn f(x: u64) ((bool, u64) | void) = { return (true, x); }; +export fn main() i32 = { return 0; }; diff --git a/test/wcc/data/twiden_bare_literal_reject/case.ww b/test/wcc/data/twiden_bare_literal_reject/case.ww new file mode 100644 index 00000000..2dc27918 --- /dev/null +++ b/test/wcc/data/twiden_bare_literal_reject/case.ww @@ -0,0 +1,12 @@ +//ww:error "tuple-in-union variant tag unresolved" +package main; +fn mk() ((i64, i64) | nomem) = { + return (5, 6); +}; +export fn main() i32 = { + match (mk()) { + case let t: (i64, i64) => { if (t.0 != 5) { return 1; }; }; + case => return 2; + }; + return 0; +};