diff --git a/Makefile b/Makefile index f5d5cfbe..02f950c6 100644 --- a/Makefile +++ b/Makefile @@ -288,13 +288,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_zerotest_run \ $(BIN)/test_ampfncollide_run \ $(BIN)/test_trycallcollide_run \ - $(BIN)/test_taggedidx_run \ $(BIN)/test_fnptrcollide_run \ - $(BIN)/test_tagnorm_run \ - $(BIN)/test_globtagisas_run \ - $(BIN)/test_globtagreassign_run \ - $(BIN)/test_globstructwiden_run \ - $(BIN)/test_globtagwiden_run \ $(BIN)/test_arr_ptr_global \ $(BIN)/test_def_arr_infer_len \ $(BIN)/test_def_arr_len \ @@ -353,26 +347,17 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_signed_data_emit \ $(BIN)/test_signed_data_emit_run \ $(BIN)/test_tagged_call_arg \ - $(BIN)/test_tagged_call_arg_run \ - $(BIN)/test_tryprop_tag_remap_run \ $(BIN)/test_nullable_try_run \ $(BIN)/test_nullable_assert_run \ - $(BIN)/test_nested_union_widen_run \ $(BIN)/test_sret_struct_return \ $(BIN)/test_tagged_sret_run \ - $(BIN)/test_void_error_singleton_run \ $(BIN)/test_tagged_memarg_run \ $(BIN)/test_tagscr_sizes_run \ - $(BIN)/test_is_nonident_run \ - $(BIN)/test_match_nonident_idx_run \ $(BIN)/test_global_sret_run \ $(BIN)/test_sret_narrow_field \ $(BIN)/test_match_slice_variant \ - $(BIN)/test_match_slice_variant_run \ $(BIN)/test_array_zeroinit_run \ $(BIN)/test_alias_accept_run \ - $(BIN)/test_idx_tagged_field_run \ - $(BIN)/test_tagged_widen_arg_run \ $(BIN)/test_alias_cgen_b5_run \ $(BIN)/test_alias_cgen_b6_run \ $(BIN)/test_alias_emit_b7_run \ @@ -381,7 +366,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_const_slice_aggregate_run \ $(BIN)/test_append_structlit_evalorder_run \ $(BIN)/test_xmod_fnptr_const_run \ - $(BIN)/test_tagged_structlit_payload_run \ $(BIN)/test_forrange_fieldbase_run \ $(BIN)/test_errtype_compare \ $(BIN)/test_intbinop_mismatch \ @@ -394,7 +378,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_match_4arm_cross_module \ $(BIN)/test_match_4arm_cross_module_run \ $(BIN)/test_free_noop_run \ - $(BIN)/test_variant_typekey_run \ $(BIN)/test_enum_modshadow \ $(BIN)/test_struct_modshadow \ $(BIN)/test_def_modshadow \ @@ -479,8 +462,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \ $(BIN)/test_single_field_struct_zeroinit \ $(BIN)/test_structvariant_largeunion_return \ $(BIN)/test_narrow_alias_deref_store \ - $(BIN)/test_taggedderefstore_run \ - $(BIN)/test_taggedglobalindex_run \ $(BIN)/test_bufio_vstream_run \ $(BIN)/test_log_vstream_run \ $(BIN)/test_use_promote_alias \ @@ -833,40 +814,6 @@ $(BIN)/test_structlocal_frame: test/wcc/989_structlocal_frame.c \ $(BIN)/w6c $(BIN)/w6c_ww | $(BIN) $(CC) $(CFLAGS) -o $@ $< -# 989_taggedderefstore_run (#17): a deref-target store `*p = v` (p:*tagged) -# sized the write off the pointee and emitted ONE fldstoreop — rhs landed in -# the tag word, payload dropped, the union discriminant corrupted. BOTH-stage- -# wrong and byte-id-green (#263-class, gate-blind): cstage cmd/w6c/cgen.c #17 -# deref else-branch + wwstage cgenexpr.ww #17 single-store tail emitted the -# SAME wrong asm. #17 routes the tagged-pointee deref store through the widener -# (cg_widen_tagged_store / cgwidentaggedstore) via the shared tag scratch, then -# word-copies scratch -> *p (mirror of the index-element tagged arm). Builds+ -# runs on BOTH driver twins (rule-10), pinning the absolute 0; scalarctl pins -# the unchanged non-tagged single-store path. -$(BIN)/test_taggedderefstore_run: test/wcc/989_taggedderefstore_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_taggedglobalindex_run (#16): an indexed store into a GLOBAL tagged array -# `gs[i] = v` (gs:[N](A|B)) — the indexed-tagged-element assign arm (cmd/w6c/ -# cgen.c ~6502) lacked the `isglobal -> LEAQ name(SB)` base branch the scalar -# `arr[i]=v` element arm (~6767) already had, so the global base resolved to a -# junk BP offset and the widened store landed nowhere; cstage silently returned -# 0 while wwstage (cgenexpr.ww index arm, already global-aware) returned the -# correct value — a cs!=ww divergence the byte-id gate could SEE. #16 aligns -# cstage UP with the mirrored isglobal branch. Builds+runs on BOTH driver twins -# (rule-10), pinning the absolute 0; loc_ctl + scalar_ctl pin the unchanged -# frame-base and scalar-template paths. -$(BIN)/test_taggedglobalindex_run: test/wcc/989_taggedglobalindex_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_arrlit_tail_zero_run (#13): an under-length array literal zero-fills the # unspecified tail, not the last value. Builds+runs on BOTH driver twins # (rule-10), pinning the absolute value (pre-fix ww tail = last value). @@ -990,26 +937,6 @@ $(BIN)/test_trycallcollide_run: test/wcc/989_trycallcollide_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -# 989_taggedidx_run (F7-c6, #23): a tagged-union element indexed off a call -# or slice base must load the full tag+payload cursor. Builds+runs on BOTH -# driver twins (rule-10). -$(BIN)/test_taggedidx_run: test/wcc/989_taggedidx_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_tagnorm_run (F1, #1/#3): tagged-union type-set normalization (never-drop, -# dedup, single-collapse, nullable fold) must be symmetric — size()/align() -# fold + tag numbering. wwstage gated. See the test header. -$(BIN)/test_tagnorm_run: test/wcc/989_tagnorm_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_fnptrcollide_run (F7-c7, #14): a value ident whose leaf collides with # a fn name must not be mis-folded into the fn's TEXT reloc. Build-must-fail # on BOTH driver twins (rule-10); wwstage gated. See the test header. @@ -1020,49 +947,6 @@ $(BIN)/test_fnptrcollide_run: test/wcc/989_fnptrcollide_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -# 989_globtagisas_run (F8-c3, #18): `is`/`as` on a module-global tagged -# ident must read tag/payload from g(SB), not saved-BP. MIXED per-half: -# `is` align-UP (cs==ww); `as` #263 ww-runtime-correct (cs!=ww residual, -# cstage half = task #46). Per-driver expected rows. See the test header. -$(BIN)/test_globtagisas_run: test/wcc/989_globtagisas_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_globtagreassign_run (F8-c5, #32, #263): reassigning a module-global -# tagged ident (g = expr) must store tag+payload. ww-runtime-correct; cstage -# drops the store (cs!=ww residual, cstage half = task #41). Per-driver -# expected rows. See the test header. -$(BIN)/test_globtagreassign_run: test/wcc/989_globtagreassign_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_globstructwiden_run (F8-c7, #50, #263): widening a module-global struct -# ident into a tagged union must copy the full payload. ww-runtime-correct; -# cstage zero-fills the payload (cs!=ww residual, cstage half = task #43). -$(BIN)/test_globstructwiden_run: test/wcc/989_globstructwiden_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_globtagwiden_run (F8-c8, #51, #263): widening a module-global tagged -# ident into a wider union must copy the whole box as the nested payload. -# ww-runtime-correct; cstage copies frame garbage — non-deterministic, so the -# cstage rows assert only divergence (cs!=ww, build-ok), cstage half = task #44. -$(BIN)/test_globtagwiden_run: test/wcc/989_globtagwiden_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 $@ $< - $(BIN)/test_let_global: test/wcc/630_let_global.c $(BIN)/ww $(BIN)/w6c \ $(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< @@ -2068,24 +1952,6 @@ $(BIN)/test_tagged_call_arg: test/wcc/720_tagged_call_arg.c \ $(BIN)/w6c $(BIN)/w6c_ww | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_tagged_call_arg_run: test/wcc/924_tagged_call_arg_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_tryprop_tag_remap_run: test/wcc/925_tryprop_tag_remap_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_nested_union_widen_run: test/wcc/925_nested_union_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_nullable_try_run: test/wcc/949_nullable_try_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -2108,12 +1974,6 @@ $(BIN)/test_tagged_sret_run: test/wcc/926_tagged_sret_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_void_error_singleton_run: test/wcc/949_void_error_singleton_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_memarg_run: test/wcc/929_tagged_memarg_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -2126,18 +1986,6 @@ $(BIN)/test_tagscr_sizes_run: test/wcc/926_tagscr_sizes_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_is_nonident_run: test/wcc/927_is_nonident_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_match_nonident_idx_run: test/wcc/928_match_nonident_idx_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_global_sret_run: test/wcc/940_global_sret_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -2152,12 +2000,6 @@ $(BIN)/test_match_slice_variant: test/wcc/722_match_slice_variant.c \ $(BIN)/w6c $(BIN)/w6c_ww | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_match_slice_variant_run: test/wcc/926_match_slice_variant_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_str_forrange_loopvar_run: test/wcc/940_str_forrange_loopvar_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -2176,18 +2018,6 @@ $(BIN)/test_alias_accept_run: test/wcc/944_alias_accept_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_idx_tagged_field_run: test/wcc/944_idx_tagged_field_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_widen_arg_run: test/wcc/944_tagged_widen_arg_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_cgen_b5_run: test/wcc/944_alias_cgen_b5_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -2233,12 +2063,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_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 \ - $(LIB)/libwwrt.a | $(BIN) - $(CC) $(CFLAGS) -o $@ $< - $(BIN)/test_forrange_fieldbase_run: test/wcc/937_forrange_fieldbase_run.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \ @@ -2430,12 +2254,6 @@ $(BIN)/test_free_noop_run: test/wcc/930_free_noop_run.c \ $(LIB)/libwwrt.a | $(BIN) $(CC) $(CFLAGS) -o $@ $< -$(BIN)/test_variant_typekey_run: test/wcc/931_variant_typekey_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_use_promote_alias: test/wcc/699_use_promote_alias.c \ $(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \ $(LIB)/libwwrt.a | $(BIN) @@ -3098,7 +2916,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 = 93 +LANGBYTEID_EXPECTED_MIN = 113 $(if $(LANGBYTEID_FILES),,$(error test-lang-byteid: empty corpus)) test-lang-byteid: all @set -e; \ diff --git a/test/lang/globstructwiden_test.ww b/test/lang/globstructwiden_test.ww new file mode 100644 index 00000000..8fb91143 --- /dev/null +++ b/test/lang/globstructwiden_test.ww @@ -0,0 +1,29 @@ +// globstructwiden_test — F8-c7 (#50, #263): widening a module-GLOBAL struct +// ident into a tagged union must copy the full payload into the box, not just +// word0. Was a cat-A both-stage-wrong, now CLOSED both stages (.s byte-id). +// Migrated from test/wcc/989_globstructwiden_run.c (K_RUN value rows). The +// global lives at module scope so the widen exercises the g(SB) source arm. + +package globstructwiden_test; + +type pt = struct { a: i64, b: i64 }; +type tri = struct { a: i64, b: i64, c: i64 }; + +let gp16: pt = pt { a = 5, b = 6 }; +let gp24: tri = tri { a = 5, b = 6, c = 7 }; + +@test fn widen_16() void = { + let x: (pt | str) = gp16; + match (x) { + case let q: pt => { assert(q.a == 5 && q.b == 6); }; + case str => { assert(false); }; + }; +}; + +@test fn widen_24() void = { + let x: (tri | str) = gp24; + match (x) { + case let q: tri => { assert(q.a == 5 && q.b == 6 && q.c == 7); }; + case str => { assert(false); }; + }; +}; diff --git a/test/lang/globtagisas_test.ww b/test/lang/globtagisas_test.ww new file mode 100644 index 00000000..cea4ae4c --- /dev/null +++ b/test/lang/globtagisas_test.ww @@ -0,0 +1,30 @@ +// globtagisas_test — F8-c3 (#18) + #46: `is`/`as` on a module-GLOBAL tagged- +// union ident must read tag/payload from g(SB), not the saved-BP word. The `is` +// half was align-UP (ww read 0(BP)); the `as` half was #263 both-wrong (cstage +// spilled uninitialized DX). Both CLOSED, .s byte-id. Migrated from +// test/wcc/989_globtagisas_run.c (K_RUN value rows). Globals kept at module +// scope so the read fires the g(SB) path. + +package globtagisas_test; + +let g_is: (i64 | bool) = 5; +let g_asi: (i64 | bool) = 42; +let g_ass: (str | i64) = "hello"; + +@test fn is_match() void = { + assert(g_is is i64); +}; + +@test fn is_nomatch() void = { + assert(!(g_is is bool)); +}; + +@test fn as_i64() void = { + let x: i64 = g_asi as i64; + assert(x == 42); +}; + +@test fn as_str() void = { + let s: str = g_ass as str; + assert(len(s) == 5); +}; diff --git a/test/lang/globtagreassign_test.ww b/test/lang/globtagreassign_test.ww new file mode 100644 index 00000000..20c2d03b --- /dev/null +++ b/test/lang/globtagreassign_test.ww @@ -0,0 +1,34 @@ +// globtagreassign_test — reassigning a module-GLOBAL tagged-union stores the +// full box at g(SB), not just the tag word. Migrated from +// test/wcc/989_globtagreassign_run.c (K_RUN value rows, cs==ww). Globals kept +// at module scope; each @test reassigns then matches. + +package globtagreassign_test; + +let g_i64: (i64 | void) = 0; +let g_str: (str | i64) = 0; +let g_union: (i64 | bool) = 0; + +@test fn reassign_i64() void = { + g_i64 = 7; + match (g_i64) { + case let n: i64 => { assert(n == 7); }; + case void => { assert(false); }; + }; +}; + +@test fn reassign_str() void = { + g_str = "hello"; + match (g_str) { + case let s: str => { assert(len(s) == 5); }; + case i64 => { assert(false); }; + }; +}; + +@test fn reassign_union() void = { + g_union = 5; + match (g_union) { + case let n: i64 => { assert(n == 5); }; + case bool => { assert(false); }; + }; +}; diff --git a/test/lang/globtagwiden_test.ww b/test/lang/globtagwiden_test.ww new file mode 100644 index 00000000..8740ba5c --- /dev/null +++ b/test/lang/globtagwiden_test.ww @@ -0,0 +1,37 @@ +// globtagwiden_test — F8-c8 (#51, #263): widening a module-GLOBAL tagged-union +// ident into a wider union must copy gi's whole box as the nested payload, not +// store its tag as the payload. CLOSED both stages, .s byte-id. Migrated from +// test/wcc/989_globtagwiden_run.c (value rows). Globals kept at module scope. + +package globtagwiden_test; + +type inner = (i64 | bool); + +let gi47: inner = 47; +let gi99: inner = 99; + +@test fn nest_47() void = { + let x: (inner | str) = gi47; + match (x) { + case let n: inner => { + match (n) { + case let v: i64 => { assert(v == 47); }; + case bool => { assert(false); }; + }; + }; + case str => { assert(false); }; + }; +}; + +@test fn nest_99() void = { + let x: (inner | str) = gi99; + match (x) { + case let n: inner => { + match (n) { + case let v: i64 => { assert(v == 99); }; + case bool => { assert(false); }; + }; + }; + case str => { assert(false); }; + }; +}; diff --git a/test/lang/idx_tagged_field_test.ww b/test/lang/idx_tagged_field_test.ww new file mode 100644 index 00000000..aa1a8443 --- /dev/null +++ b/test/lang/idx_tagged_field_test.ww @@ -0,0 +1,52 @@ +// idx_tagged_field_test — xs[i].f tagged-field read (const/var index, 2-tag +// union) and assign-back through a pointer. Migrated from +// test/wcc/944_idx_tagged_field_run.c (value rows; the C driver asserted cs==ww +// .s). The >32B / multi-word / float-payload arms LOUD-STOP (#58) and live as +// runww //ww:error carriers (test/wcc/data/idxtagfield_*); #114 graduates them. + +package idx_tagged_field_test; + +type opt = (void | size); +type opt3 = (void | bool | size); +type row = struct { id: int, m: opt }; +type row3 = struct { id: int, m: opt3 }; + +@test fn const_read() void = { + let xs: [2]row = [ + row { id = 1, m = 7: size }, + row { id = 2, m = 8: size }, + ]; + let v: size = xs[0].m as size; + assert(v == 7); +}; + +@test fn var_read() void = { + let xs: [2]row = [ + row { id = 1, m = 7: size }, + row { id = 2, m = 8: size }, + ]; + let i: size = 1; + let v: size = xs[i].m as size; + assert(v == 8); +}; + +@test fn tag2_read() void = { + let xs: [2]row3 = [ + row3 { id = 1, m = 5: size }, + row3 { id = 2, m = 9: size }, + ]; + let i: size = 1; + let v: size = xs[i].m as size; + assert(v == 9); +}; + +@test fn assign_back() void = { + let xs: [2]row = [ + row { id = 1, m = 99: size }, + row { id = 2, m = 2: size }, + ]; + xs[0].m = 8: size; + let p: *row = &xs[0]; + let v: size = p.m as size; + assert(v == 8); +}; diff --git a/test/lang/is_nonident_test.ww b/test/lang/is_nonident_test.ww new file mode 100644 index 00000000..1416604a --- /dev/null +++ b/test/lang/is_nonident_test.ww @@ -0,0 +1,90 @@ +// is_nonident_test — `e is T` on non-ident scrutinees (indexed, dot-field, +// call-result, slice-variant, nullable (*T|void) on dot-field and ident). Each +// pins both polarities. Migrated from test/wcc/927_is_nonident_run.c (value +// rows; the C driver already asserted cs==ww .s, so byte-id holds). + +package is_nonident_test; + +type p48 = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64 }; +type small = (p48 | bool); +type holder = struct { v: small, k: i64 }; + +type val = (i64 | bool); +type valslice = (i64 | []u8); + +type t = struct { a: i64 }; +type maybe = (*t | void); +type nholder = struct { m: maybe, k: i64 }; + +fn mk(flag: bool) val = { + if (flag) { + let b: bool = true; + return b; + }; + let n: i64 = 7; + return n; +}; + +@test fn indexed() void = { + let xs: []small; + let b: bool = true; + let v: small = b; + append(xs, v); + assert(xs.len != 0 && xs[xs.len - 1] is bool); + assert(!(xs[xs.len - 1] is p48)); + assert(xs[0] is bool); +}; + +@test fn dot_field() void = { + let b: bool = true; + let h: holder = holder { v = b, k = 5 }; + assert(h.v is bool); + assert(!(h.v is p48)); +}; + +@test fn call_result() void = { + assert(mk(true) is bool); + assert(mk(false) is i64); + assert(!(mk(true) is i64)); +}; + +@test fn slice_variant_indexed() void = { + let xs: []valslice; + let buf: [2]u8 = [1u8, 2u8]; + let s: []u8 = buf[0:2]; + let v: valslice = s; + append(xs, v); + let n: i64 = 5; + let w: valslice = n; + append(xs, w); + assert(xs[0] is []u8); + assert(xs[1] is i64); + assert(!(xs[0] is i64)); +}; + +@test fn nullable_dot_field() void = { + let v: t = t { a = 5 }; + let h: nholder = nholder { m = &v, k = 1 }; + assert(h.m is *t); + assert(!(h.m is void)); + let h2: nholder = nholder { m = void, k = 2 }; + assert(!(h2.m is *t)); + assert(h2.m is void); +}; + +@test fn nullable_ident() void = { + let v: t = t { a = 5 }; + let m: maybe = &v; + assert(m is *t); + assert(!(m is void)); + let m2: maybe = void; + assert(!(m2 is *t)); + assert(m2 is void); +}; + +@test fn ident_control() void = { + let b: bool = true; + let v: val = b; + assert(v is bool); + assert(!(v is i64)); +}; diff --git a/test/lang/match_nonident_idx_test.ww b/test/lang/match_nonident_idx_test.ww new file mode 100644 index 00000000..a9829503 --- /dev/null +++ b/test/lang/match_nonident_idx_test.ww @@ -0,0 +1,102 @@ +// match_nonident_idx_test — match on an N_INDEX scrutinee whose base is a +// non-ident (struct-field slice, chained-dot slice, 56B-slot regex-inst union), +// plus ident/ident-base controls. Pins arm-order independence and the stamped- +// carrier resolve at any base depth. Migrated from +// test/wcc/928_match_nonident_idx_run.c (value rows; C driver asserted cs==ww .s). + +package match_nonident_idx_test; + +type va = void; +type vb = bool; +type vc = size; +type tu = (va | vb | vc); + +type holder = struct { xs: []tu, n: size }; +type inner = struct { xs: []tu, k: size }; +type outer = struct { in_: inner, n: size }; + +type big = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64 }; +type ilit = struct { r: i64 }; +type imatch = void; +type inst = (big | ilit | imatch); +type re = struct { insts: []inst, n: i64 }; + +@test fn field_slice_idx() void = { + let sl: []tu = [(true: vb), ((7: size): vc)]; + let h: holder; + h.xs = sl; + h.n = 2; + match (h.xs[0]) { + case let b: vb => { assert(b: bool); }; + case => { assert(false); }; + }; + match (h.xs[1]) { + case let s: vc => { assert((s: size) == 7); }; + case => { assert(false); }; + }; + match (h.xs[0]) { + case va => { assert(false); }; + case let b: vb => { assert(b: bool); }; + case vc => { assert(false); }; + }; +}; + +@test fn regex_inst_56b() void = { + let sl: []inst; + let l: ilit = ilit { r = 65 }; + let v: inst = l; + append(sl, v); + let m: inst = void: imatch; + append(sl, m); + let r: re; + r.insts = sl; + r.n = 2; + match (r.insts[0]) { + case let x: ilit => { assert(x.r == 65); }; + case => { assert(false); }; + }; + match (r.insts[1]) { + case imatch => { }; + case => { assert(false); }; + }; + match (r.insts[0]) { + case big => { assert(false); }; + case let x: ilit => { assert(x.r == 65); }; + case imatch => { assert(false); }; + }; +}; + +@test fn dot_depth_idx() void = { + let sl: []tu = [(true: vb), ((9: size): vc)]; + let o: outer; + o.in_.xs = sl; + o.in_.k = 1; + o.n = 2; + match (o.in_.xs[0]) { + case let b: vb => { assert(b: bool); }; + case => { assert(false); }; + }; + match (o.in_.xs[1]) { + case va => { assert(false); }; + case let s: vc => { assert((s: size) == 9); }; + case vb => { assert(false); }; + }; +}; + +@test fn ident_and_identbase_controls() void = { + let v: tu = (true: vb); + match (v) { + case let b: vb => { assert(b: bool); }; + case => { assert(false); }; + }; + let arr: [2]tu = [(true: vb), ((7: size): vc)]; + match (arr[1]) { + case let s: vc => { assert((s: size) == 7); }; + case => { assert(false); }; + }; + let sl: []tu = [(true: vb), ((7: size): vc)]; + match (sl[0]) { + case let b: vb => { assert(b: bool); }; + case => { assert(false); }; + }; +}; diff --git a/test/lang/match_slice_variant_test.ww b/test/lang/match_slice_variant_test.ww new file mode 100644 index 00000000..b8af5038 --- /dev/null +++ b/test/lang/match_slice_variant_test.ww @@ -0,0 +1,116 @@ +// match_slice_variant_test — a `(scalar | []T)` match must select the right arm +// and preserve the slice payload (len + first byte) across u8/i8/i32/u64/rune, +// in both variant orders, and compose with a trailing str arm. Migrated from +// test/wcc/926_match_slice_variant_run.c (value rows, cs==ww byte-id). + +package match_slice_variant_test; + +fn pick_u8(n: (u8 | []u8)) i32 = { + match (n) { + case let c: u8 => return c: i32; + case let s: []u8 => return 100i32 + (s.len: i32) + (s[0]: i32); + }; + return -1; +}; + +fn pick_slice_first(n: ([]u8 | u8)) i32 = { + match (n) { + case let s: []u8 => return 100i32 + (s.len: i32) + (s[0]: i32); + case let c: u8 => return c: i32; + }; + return -1; +}; + +fn pick_i8(n: (i8 | []i8)) i32 = { + match (n) { + case let c: i8 => return c: i32; + case let s: []i8 => return 100i32 + (s.len: i32) + (s[0]: i32); + }; + return -1; +}; + +fn pick_i32(n: (i32 | []i32)) i32 = { + match (n) { + case let c: i32 => return c; + case let s: []i32 => return 1000i32 + (s.len: i32) + s[0]; + }; + return -1; +}; + +fn pick_u64(n: (u64 | []u64)) i32 = { + match (n) { + case let c: u64 => return c: i32; + case let s: []u64 => return 1000i32 + (s.len: i32) + (s[0]: i32); + }; + return -1; +}; + +fn pick_rune(n: (rune | []rune)) i32 = { + match (n) { + case let r: rune => return r: i32; + case let s: []rune => return 1000i32 + (s.len: i32) + (s[0]: i32); + }; + return -1; +}; + +fn pick3(n: (u8 | []u8 | str)) i32 = { + match (n) { + case let c: u8 => return c: i32; + case let s: []u8 => return 200i32 + (s.len: i32) + (s[0]: i32); + case let t: str => return 300i32 + (t.len: i32); + }; + return -1; +}; + +@test fn u8_slice_u8() void = { + let buf: [3]u8; + buf[0] = 7u8; buf[1] = 8u8; buf[2] = 9u8; + assert(pick_u8(42u8) == 42); + assert(pick_u8(buf[0:3]) == 110); +}; + +@test fn slice_u8_then_u8() void = { + let buf: [3]u8; + buf[0] = 7u8; buf[1] = 8u8; buf[2] = 9u8; + assert(pick_slice_first(buf[0:3]) == 110); + assert(pick_slice_first(42u8) == 42); +}; + +@test fn i8_slice_i8() void = { + let buf: [2]i8; + buf[0] = 5i8; buf[1] = 6i8; + assert(pick_i8(11i8) == 11); + assert(pick_i8(buf[0:2]) == 107); +}; + +@test fn i32_slice_i32() void = { + let buf: [2]i32; + buf[0] = 33i32; buf[1] = 44i32; + assert(pick_i32(77i32) == 77); + assert(pick_i32(buf[0:2]) == 1035); +}; + +@test fn u64_slice_u64() void = { + let buf: [2]u64; + buf[0] = 41u64; buf[1] = 42u64; + assert(pick_u64(99u64) == 99); + assert(pick_u64(buf[0:2]) == 1043); +}; + +@test fn rune_slice_rune() void = { + let buf: [2]rune; + buf[0] = 'a'; buf[1] = 'b'; + assert(pick_rune('Z') == 90); + assert(pick_rune(buf[0:2]) == 1099); +}; + +@test fn u8_slice_u8_str() void = { + let buf: [3]u8; + buf[0] = 1u8; buf[1] = 2u8; buf[2] = 3u8; + let a: i32 = pick3(5u8); + let b: i32 = pick3(buf[0:3]); + let c: i32 = pick3("hi"); + assert(a == 5); + assert(b == 204); + assert(c == 302); +}; diff --git a/test/lang/nested_union_widen_test.ww b/test/lang/nested_union_widen_test.ww new file mode 100644 index 00000000..2e7110b7 --- /dev/null +++ b/test/lang/nested_union_widen_test.ww @@ -0,0 +1,58 @@ +// nested_union_widen_test — widening a nested named error-union (inner = !(i32|bool)) +// into an enclosing (size | inner) union must keep the inner box distinguishable +// and destructurable across outer-arm select, fn-return, and let-init. Migrated +// from test/wcc/925_nested_union_widen_run.c (value rows). The same-shape +// collision row (two distinct names !(i32|bool)) loud-stops both stages and lives +// at test/wcc/data/nestunion_collision/ as a runww //ww:error carrier. + +package nested_union_widen_test; + +type inner = !(i32 | bool); + +fn g_dr() (size | inner) = { + let e: inner = (5i32: inner); + return e; +}; + +@test fn outer_arm_select() void = { + let e: inner = (true: inner); + let r: (size | inner) = e; + match (r) { + case let n: size => { assert(false); }; + case let x: inner => { }; + }; +}; + +@test fn destructure_return() void = { + match (g_dr()) { + case let s: size => { assert(false); }; + case let x: inner => { + match (x) { + case let i: i32 => { assert(i == 5); }; + case let b: bool => { assert(false); }; + }; + }; + }; +}; + +@test fn destructure_let() void = { + let e: inner = (7i32: inner); + let r: (size | inner) = e; + match (r) { + case let n: size => { assert(false); }; + case let x: inner => { + match (x) { + case let i: i32 => { assert(i == 7); }; + case let b: bool => { assert(false); }; + }; + }; + }; +}; + +@test fn single_variant() void = { + let r: (i32 | bool) = 7i32; + match (r) { + case let n: i32 => { assert(n == 7); }; + case let b: bool => { assert(false); }; + }; +}; diff --git a/test/lang/tagged_call_arg_test.ww b/test/lang/tagged_call_arg_test.ww new file mode 100644 index 00000000..868a0a31 --- /dev/null +++ b/test/lang/tagged_call_arg_test.ww @@ -0,0 +1,70 @@ +// tagged_call_arg_test — #21: passing a tagged-union arg sourced from a fn call +// that returns the same tagged union must not drop the DX/CX/R8 payload words. +// Pins CALL-source (4-variant + 2-variant) and the IDENT-source fast-path that +// must stay byte-identical, plus a tagged-CALL-then-scalar pop-count row. +// Migrated from test/wcc/924_tagged_call_arg_run.c (value rows, cs==ww). + +package tagged_call_arg_test; + +type done = !void; +type more = !void; +type invalid = !void; +type oserror = !i32; + +fn yield_rune() (rune | done | more | invalid) = { + return 65u32: rune; +}; +fn dispatch4(v: (rune | done | more | invalid)) i32 = { + match (v) { + case let r: rune => return r: i32; + case let d: done => return -1; + case let m: more => return -2; + case let e: invalid => return -3; + }; + return -99; +}; + +fn yield_ptr() (*u8 | oserror) = { + let p: *u8 = nil; + return p; +}; +fn yield_err() (*u8 | oserror) = { + let e: oserror = 42i32: oserror; + return e; +}; +fn dispatch2(v: (*u8 | oserror)) i32 = { + match (v) { + case let p: *u8 => return 7; + case let e: oserror => return e: i32; + }; + return -99; +}; + +fn use2(v: (*u8 | oserror), k: i32) i32 = { + match (v) { + case let p: *u8 => return k + 1; + case let e: oserror => return k - 1; + }; + return -99; +}; + +@test fn variant4_call_source_rune() void = { + assert(dispatch4(yield_rune()) == 65); +}; + +@test fn variant4_ident_source_rune() void = { + let v: (rune | done | more | invalid) = 65u32: rune; + assert(dispatch4(v) == 65); +}; + +@test fn variant2_call_source_ptr() void = { + assert(dispatch2(yield_ptr()) == 7); +}; + +@test fn variant2_call_source_err() void = { + assert(dispatch2(yield_err()) == 42); +}; + +@test fn tagged_call_then_scalar() void = { + assert(use2(yield_ptr(), 41) == 42); +}; diff --git a/test/lang/tagged_structlit_payload_test.ww b/test/lang/tagged_structlit_payload_test.ww new file mode 100644 index 00000000..ad850dc3 --- /dev/null +++ b/test/lang/tagged_structlit_payload_test.ww @@ -0,0 +1,378 @@ +// tagged_structlit_payload_test — #23/#33/#38a: constructing a tagged union from +// an inline struct literal whose fields are themselves tagged (void|size) / +// (void|str) must fill the payload at the WIDENED slot's payload base, copy every +// payload word (not just AX), and read/assign tagged leaves down a dot-spine. +// Migrated from test/wcc/938_tagged_structlit_payload_run.c (value rows, cs==ww +// byte-id). Types are per-row-prefixed because the C rows reused names with +// different shapes. +// +// voidstr_field + recursion_torture do an inner match on a tagged FIELD scrutinee +// (o.s, m.t) — historically tagged "match-on-tagged-struct-FIELD cs≠ww" (aliases +// regex-arc #26 / cont.9 #42 match-field-cursor); the retired C driver flagged +// them noasm=1. Divergence tag (master 6699158) STALE at HEAD: byte-id verified +// cs==ww AND T1 value-correct. These rows are the REGRESSION SENTINEL for the +// match-on-tagged-field divergence (former #26), closed no-reproducer-at-HEAD with +// attribution to the #15/#17 tagged-field-via-ptr cluster INFERRED (not diff- +// proven) — a resurgence trips this byte-id row. Reopen with a concrete shape if +// it fails. + +package tagged_structlit_payload_test; + +type ir_lit = rune; +type ir_rep = struct { id: size, origin: size, min: (void | size), max: (void | size) }; +type ir_inst = (ir_lit | ir_rep); + +type ra_lit = rune; +type ra_rep3 = struct { id: size, min: (void | size), max: (void | size) }; +type ra_inst = (ra_lit | ra_rep3); + +type to_only = struct { m: (void | size) }; +type to_u = (rune | to_only); + +type pb_lit = rune; +type pb_rep3 = struct { id: size, min: (void | size), max: (void | size) }; +type pb_inst = (pb_lit | pb_rep3); + +type sat_lit = rune; +type sat_named = struct { s: str, m: (void | size) }; +type sat_inst = (sat_lit | sat_named); + +type vp_lit = rune; +type vp_rep3 = struct { id: size, min: (void | size), max: (void | size) }; +type vp_inst = (vp_rep3 | vp_lit); + +type sec_lit = rune; +type sec_rep1 = struct { id: size, m: (void | size) }; +type sec_inst = (sec_lit | sec_rep1); + +type nsf_inner = struct { a: size, b: size }; +type nsf_outer = struct { id: size, in_: nsf_inner }; +type nsf_u = (rune | nsf_outer); + +type ea_st = struct { a: u64, m: (void | size) }; + +type tto_d2 = struct { m: (void | size), n: (void | i64), z: u64 }; + +type nat_inn = struct { v: u64 }; +type nat_outt = struct { i: nat_inn, m: (void | size) }; + +type pb8_one = struct { a: size }; +type pb8_u = (rune | pb8_one); + +type sn_plain = struct { a: size, f: f64, s: str }; +type sn_u = (rune | sn_plain); + +type clr_rep = struct { id: size, min: (void | size), name: (void | str) }; +type clr_outer = struct { tag: size, r: clr_rep }; + +type cas_rep = struct { id: size, min: (void | size) }; +type cas_outer = struct { tag: size, r: cas_rep }; + +type cd2_rep = struct { id: size, min: (void | size) }; +type cd2_mid = struct { m: size, r: cd2_rep }; +type cd2_outer = struct { tag: size, w: cd2_mid }; + +type csv_rep = struct { id: size, nm: (void | str) }; +type csv_outer = struct { tag: size, r: csv_rep }; + +type cpr_rep = struct { id: size, min: (void | size) }; +type cpr_outer = struct { tag: size, r: cpr_rep }; + +type cpm_rep = struct { id: size, min: (void | size) }; +type cpm_outer = struct { tag: size, p: *cpm_rep }; + +type vsf_st = struct { id: size, s: (void | str) }; +type vsf_u = (rune | vsf_st); + +type leaf = struct { p: size }; +type t1 = (void | leaf); +type rt_mid = struct { t: t1 }; +type t2 = (void | rt_mid); +type rt_outr = struct { id: size, t: t2 }; +type rt_u = (rune | rt_outr); + +fn take_uib(v: (void | size)) i32 = { + if (v is size) { return (v as size): i32; }; + return -1; +}; + +@test fn inst_repeat_shape() void = { + let v: ir_inst = ir_rep { id = 1: size, origin = 2: size, min = void, max = 5: size }; + match (v) { + case let ir: ir_rep => { + assert(ir.id == 1); + assert(ir.origin == 2); + assert(ir.min is void); + assert(ir.max is size); + assert(ir.max as size == 5); + }; + case let l: ir_lit => { assert(false); }; + }; +}; + +@test fn rep3_allsize() void = { + let v: ra_inst = ra_rep3 { id = 1: size, min = 2: size, max = 5: size }; + match (v) { + case let ir: ra_rep3 => { + assert(ir.id == 1); + assert(ir.min is size); + assert(ir.min as size == 2); + assert(ir.max is size); + assert(ir.max as size == 5); + }; + case let l: ra_lit => { assert(false); }; + }; +}; + +@test fn tagged_only16() void = { + let v: to_u = to_only { m = 7: size }; + match (v) { + case let o: to_only => { + assert(o.m is size); + assert(o.m as size == 7); + }; + case => { assert(false); }; + }; +}; + +@test fn prebound_tagged_field() void = { + let mn: (void | size) = 2: size; + let v: pb_inst = pb_rep3 { id = 3: size, min = mn, max = void }; + match (v) { + case let ir: pb_rep3 => { + assert(ir.id == 3); + assert(ir.min is size); + assert(ir.min as size == 2); + assert(ir.max is void); + }; + case let l: pb_lit => { assert(false); }; + }; +}; + +@test fn str_and_tagged() void = { + let v: sat_inst = sat_named { s = "hi", m = 9: size }; + match (v) { + case let nm: sat_named => { + assert(len(nm.s) == 2); + assert(nm.m is size); + assert(nm.m as size == 9); + }; + case let l: sat_lit => { assert(false); }; + }; +}; + +@test fn variant_pos0() void = { + let v: vp_inst = vp_rep3 { id = 1: size, min = void, max = 8: size }; + match (v) { + case let ir: vp_rep3 => { + assert(ir.id == 1); + assert(ir.min is void); + assert(ir.max is size); + assert(ir.max as size == 8); + }; + case let l: vp_lit => { assert(false); }; + }; +}; + +@test fn slice_elem_cursorfit() void = { + let insts: []sec_inst = []; + append(insts, ('a': sec_lit)); + let v: sec_inst = sec_rep1 { id = 4: size, m = 6: size }; + append(insts, v); + assert(len(insts) == 2); + match (insts[1]) { + case let r: sec_rep1 => { + assert(r.id == 4); + assert(r.m is size); + assert(r.m as size == 6); + }; + case let l: sec_lit => { assert(false); }; + }; +}; + +@test fn nested_struct_field() void = { + let v: nsf_u = nsf_outer { id = 1: size, in_ = nsf_inner { a = 2: size, b = 3: size } }; + match (v) { + case let o: nsf_outer => { + assert(o.id == 1); + assert(o.in_.a == 2); + assert(o.in_.b == 3); + }; + case => { assert(false); }; + }; +}; + +@test fn ellipsis_autofill() void = { + let e: (void | ea_st) = ea_st { m = 5: size, ... }; + match (e) { + case let s: ea_st => { + assert(s.a == 0u64); + assert(s.m is size); + assert(s.m as size == 5); + }; + case void => { assert(false); }; + }; +}; + +@test fn two_tagged_offset0() void = { + let e: (void | tto_d2) = tto_d2 { m = 4: size, n = (-9): i64, z = 77u64 }; + match (e) { + case let s: tto_d2 => { + assert(s.m is size); + assert(s.m as size == 4); + assert(s.n is i64); + assert(s.n as i64 == -9); + assert(s.z == 77u64); + }; + case void => { assert(false); }; + }; +}; + +@test fn nested_and_tagged() void = { + let e: (void | nat_outt) = nat_outt { i = nat_inn { v = 11u64 }, m = 8: size }; + match (e) { + case let s: nat_outt => { + assert(s.i.v == 11u64); + assert(s.m is size); + assert(s.m as size == 8); + }; + case void => { assert(false); }; + }; +}; + +@test fn payload8_boundary() void = { + let v: pb8_u = pb8_one { a = 42: size }; + match (v) { + case let o: pb8_one => { assert(o.a == 42); }; + case => { assert(false); }; + }; +}; + +@test fn scalar_neighbor() void = { + let v: sn_u = sn_plain { a = 11: size, f = 2.5, s = "xyz" }; + match (v) { + case let p: sn_plain => { + assert(p.a == 11); + assert(p.f == 2.5); + assert(len(p.s) == 3); + }; + case => { assert(false); }; + }; +}; + +@test fn chained_tagged_literal_read() void = { + let o = clr_outer { tag = 4: size, r = clr_rep { id = 6: size, min = 8: size, name = void } }; + assert(o.tag == 4); + assert(o.r.id == 6); + assert(o.r.min is size); + assert(o.r.min as size == 8); + assert(o.r.name is void); +}; + +@test fn chained_tagged_assign() void = { + let o: cas_outer = cas_outer { tag = 4: size, r = cas_rep { id = 6: size, min = void } }; + o.r.min = 8: size; + assert(o.r.min is size); + assert(o.r.min as size == 8); + o.r.min = 9; + assert(o.r.min is size); + assert(o.r.min as size == 9); +}; + +@test fn chained_tagged_depth2() void = { + let o = cd2_outer { tag = 4: size, w = cd2_mid { m = 2: size, r = cd2_rep { id = 6: size, min = 5: size } } }; + assert(o.w.r.min is size); + assert(o.w.r.min as size == 5); + o.w.r.min = 8: size; + assert(o.w.m == 2); + assert(o.w.r.id == 6); + assert(o.w.r.min is size); + assert(o.w.r.min as size == 8); +}; + +@test fn chained_tagged_strvariant() void = { + let o = csv_outer { tag = 4: size, r = csv_rep { id = 6: size, nm = void } }; + o.r.nm = "hello"; + assert(o.r.nm is str); + assert((o.r.nm as str).len == 5); + match (o.r.nm) { + case let s: str => { assert(s.len == 5); }; + case void => { assert(false); }; + }; + o.r.nm = void; + assert(o.r.nm is void); + assert(o.r.id == 6); +}; + +@test fn chained_tagged_ptrroot() void = { + let o = cpr_outer { tag = 4: size, r = cpr_rep { id = 6: size, min = void } }; + let p: *cpr_outer = &o; + p.r.min = 8: size; + assert(p.r.id == 6); + assert(p.r.min is size); + assert(p.r.min as size == 8); +}; + +@test fn chained_tagged_ptrmid() void = { + let r = cpm_rep { id = 6: size, min = void }; + let o = cpm_outer { tag = 4: size, p = &r }; + o.p.min = 8: size; + assert(o.p.id == 6); + assert(o.p.min is size); + assert(o.p.min as size == 8); +}; + +@test fn untyped_int_bare_widen() void = { + let e: (void | size) = 5; + assert(e is size); + assert(e as size == 5); + let mn: (void | size) = void; + mn = 7; + assert(mn is size); + assert(mn as size == 7); + let b: (bool | u64) = 9; + assert(b is u64); + assert(take_uib(9) == 9); + let cf: (void | size) = 5: size; + assert(cf is size); + assert(cf as size == 5); + let f: (void | i64) = 9; + assert(f is i64); + assert(f as i64 == 9); +}; + +// voidstr_field/recursion_torture: regression-sentinel rows for the former-#26 +// match-on-tagged-field divergence (STALE at HEAD: byte-id cs==ww + value-correct; +// closed no-reproducer, #15/#17 attribution inferred). See the file header. +@test fn voidstr_field() void = { + let v: vsf_u = vsf_st { id = 3: size, s = "hello" }; + match (v) { + case let o: vsf_st => { + assert(o.id == 3); + match (o.s) { + case let sv: str => { assert(len(sv) == 5); }; + case void => { assert(false); }; + }; + }; + case => { assert(false); }; + }; +}; + +@test fn recursion_torture() void = { + let v: rt_u = rt_outr { id = 1: size, t = rt_mid { t = leaf { p = 9: size } } }; + match (v) { + case let o: rt_outr => { + assert(o.id == 1); + match (o.t) { + case let m: rt_mid => { + match (m.t) { + case let l: leaf => { assert(l.p == 9); }; + case void => { assert(false); }; + }; + }; + case void => { assert(false); }; + }; + }; + case => { assert(false); }; + }; +}; diff --git a/test/lang/tagged_widen_arg_test.ww b/test/lang/tagged_widen_arg_test.ww new file mode 100644 index 00000000..29922bc7 --- /dev/null +++ b/test/lang/tagged_widen_arg_test.ww @@ -0,0 +1,73 @@ +// tagged_widen_arg_test — a narrower tagged source (ident/deref/index/dot) passed +// into a wider tagged param must widen + tag-remap so the callee reads the right +// arm. The aligned set keeps i64 at tag0 in both; the remap set has i64 at tag1 +// in the source (un16b) but tag0 in the param (un3b), forcing the remap. +// Migrated from test/wcc/944_tagged_widen_arg_run.c (value rows, all cs==ww byte-id). + +package tagged_widen_arg_test; + +type s16 = struct { a: i64, b: i64 }; +type un16 = (i64 | bool); +type un3 = (i64 | bool | s16); +type un16b = (bool | i64); +type un3b = (i64 | bool | s16); +type wrap_a = struct { f: un16 }; +type wrap_b = struct { f: un16b }; + +fn take_aligned(e: un3) i64 = { + match (e) { + case let v: i64 => { return v; }; + case let b: bool => { return -1; }; + case let s: s16 => { return s.a + s.b; }; + }; +}; + +fn take_remap(e: un3b) i64 = { + match (e) { + case let v: i64 => { return v; }; + case let b: bool => { return -1; }; + case let s: s16 => { return s.a + s.b; }; + }; +}; + +@test fn ident_aligned() void = { + let v: un16 = (42: i64); + assert(take_aligned(v) == 42); +}; + +@test fn deref_aligned() void = { + let v: un16 = (42: i64); + let p: *un16 = &v; + assert(take_aligned(*p) == 42); +}; + +@test fn index_aligned() void = { + let xs: [2]un16 = [(42: i64), (7: i64)]; + assert(take_aligned(xs[0]) == 42); +}; + +@test fn dot_aligned() void = { + let w: wrap_a = wrap_a { f = (42: i64) }; + assert(take_aligned(w.f) == 42); +}; + +@test fn ident_remap() void = { + let v: un16b = (42: i64); + assert(take_remap(v) == 42); +}; + +@test fn deref_remap() void = { + let v: un16b = (42: i64); + let p: *un16b = &v; + assert(take_remap(*p) == 42); +}; + +@test fn index_remap() void = { + let xs: [2]un16b = [(42: i64), (7: i64)]; + assert(take_remap(xs[0]) == 42); +}; + +@test fn dot_remap() void = { + let w: wrap_b = wrap_b { f = (42: i64) }; + assert(take_remap(w.f) == 42); +}; diff --git a/test/lang/taggedderefstore_test.ww b/test/lang/taggedderefstore_test.ww new file mode 100644 index 00000000..3ea5f79b --- /dev/null +++ b/test/lang/taggedderefstore_test.ww @@ -0,0 +1,67 @@ +// taggedderefstore_test — #17 teeth: `*p = v` into a tagged-union pointee must +// store the full box (route through the widener + scratch word-copy), not a +// single tag-word store. Was both-stage-wrong AND byte-id-green (#263-class), +// now CLOSED both stages. Migrated from test/wcc/989_taggedderefstore_run.c +// (value rows; scalarctl pins the non-tagged single-store path stays unchanged). + +package taggedderefstore_test; + +type pair = struct { a: i64, b: i64 }; + +@test fn taglo() void = { + let v: (int | bool) = false; + let p: *(int | bool) = &v; + *p = 42; + match (v) { + case let n: int => { assert(n == 42); }; + case bool => { assert(false); }; + }; +}; + +@test fn taghi() void = { + let v: (int | bool) = 0; + let p: *(int | bool) = &v; + *p = true; + match (v) { + case int => { assert(false); }; + case let b: bool => { assert(b); }; + }; +}; + +@test fn str3w() void = { + let v: (int | str) = 0; + let p: *(int | str) = &v; + *p = "hello"; + match (v) { + case int => { assert(false); }; + case let s: str => { assert(s.len: int == 5); }; + }; +}; + +@test fn struct2w() void = { + let v: (int | pair) = 0; + let p: *(int | pair) = &v; + *p = pair { a = 0x1111, b = 0x2222 }; + match (v) { + case int => { assert(false); }; + case let q: pair => { assert(q.a == 0x1111 && q.b == 0x2222); }; + }; +}; + +@test fn nullfold() void = { + let a: int = 7; + let v: (*int | void) = void; + let p: *(*int | void) = &v; + *p = &a; + match (v) { + case let q: *int => { assert(*q == 7); }; + case void => { assert(false); }; + }; +}; + +@test fn scalarctl() void = { + let v: int = 3; + let p: *int = &v; + *p = 42; + assert(v == 42); +}; diff --git a/test/lang/taggedglobalindex_test.ww b/test/lang/taggedglobalindex_test.ww new file mode 100644 index 00000000..5255fa2b --- /dev/null +++ b/test/lang/taggedglobalindex_test.ww @@ -0,0 +1,71 @@ +// taggedglobalindex_test — #16 teeth: `gs[i] = v` into a global tagged array +// must use the global base g(SB) and store the full box. Migrated from +// test/wcc/989_taggedglobalindex_run.c (value rows; loc_ctl/scalar_ctl pin the +// frame-base and scalar-array control arms stay unperturbed). Globals kept at +// module scope. + +package taggedglobalindex_test; + +type pair = struct { a: i64, b: i64 }; + +let gs_lo: [2](int | bool) = [0, 0]; +let gs_hi: [2](int | bool) = [0, 0]; +let gs_var: [3](int | bool) = [0, 0, 0]; +let gs_str: [2](int | str) = [0, 0]; +let gs_struct: [2](int | pair) = [0, 0]; +let g_scalar: [3]int = [0, 0, 0]; + +@test fn glo_taglo() void = { + gs_lo[1] = 42; + match (gs_lo[1]) { + case let n: int => { assert(n == 42); }; + case bool => { assert(false); }; + }; +}; + +@test fn glo_taghi() void = { + gs_hi[1] = true; + match (gs_hi[1]) { + case int => { assert(false); }; + case let b: bool => { assert(b); }; + }; +}; + +@test fn glo_varidx() void = { + let i: int = 2; + gs_var[i] = 99; + match (gs_var[2]) { + case let n: int => { assert(n == 99); }; + case bool => { assert(false); }; + }; +}; + +@test fn glo_str3w() void = { + gs_str[1] = "hello"; + match (gs_str[1]) { + case int => { assert(false); }; + case let s: str => { assert(s.len: int == 5); }; + }; +}; + +@test fn glo_struct2w() void = { + gs_struct[1] = pair { a = 0x1111, b = 0x2222 }; + match (gs_struct[1]) { + case int => { assert(false); }; + case let q: pair => { assert(q.a == 0x1111 && q.b == 0x2222); }; + }; +}; + +@test fn loc_ctl() void = { + let a: [2](int | bool) = [0, 0]; + a[1] = 42; + match (a[1]) { + case let n: int => { assert(n == 42); }; + case bool => { assert(false); }; + }; +}; + +@test fn scalar_ctl() void = { + g_scalar[1] = 42; + assert(g_scalar[1] == 42); +}; diff --git a/test/lang/taggedidx_test.ww b/test/lang/taggedidx_test.ww new file mode 100644 index 00000000..91de20c6 --- /dev/null +++ b/test/lang/taggedidx_test.ww @@ -0,0 +1,48 @@ +// taggedidx_test — #23: a tagged element off a non-ident base (slice `arr[1:][0]`, +// call `getrows()[1]`) must load the full cursor, not just the tag word. The +// N_IDENT base control already worked and must not regress. Migrated from +// test/wcc/989_taggedidx_run.c (value rows). + +package taggedidx_test; + +type box = (i64 | bool); + +fn getrows() []box = { + let arr: [3]box; + arr[0] = 10; arr[1] = 50; arr[2] = 70; + return arr[0:]; +}; + +@test fn slice_tagged_idx() void = { + let arr: [3]box; + arr[0] = 10; arr[1] = 40; arr[2] = 70; + let v: box = arr[1:][0]; + let r: i64 = 0; + match (v) { + case let x: i64 => { r = x; }; + case let b: bool => { r = 99; }; + }; + assert(r == 40); +}; + +@test fn call_tagged_idx() void = { + let v: box = getrows()[1]; + let r: i64 = 0; + match (v) { + case let x: i64 => { r = x; }; + case let b: bool => { r = 99; }; + }; + assert(r == 50); +}; + +@test fn ident_tagged_idx() void = { + let arr: [3]box; + arr[0] = 10; arr[1] = 40; arr[2] = 70; + let v: box = arr[1]; + let r: i64 = 0; + match (v) { + case let x: i64 => { r = x; }; + case let b: bool => { r = 99; }; + }; + assert(r == 40); +}; diff --git a/test/lang/tagnorm_test.ww b/test/lang/tagnorm_test.ww new file mode 100644 index 00000000..5389bec9 --- /dev/null +++ b/test/lang/tagnorm_test.ww @@ -0,0 +1,80 @@ +// tagnorm_test — tagged-union normalization: nullable folds to an 8B ptr slot; +// `never` drops; duplicate variants dedup; size/align ride the normalized chain; +// and a bare `return;` void tag comes off the NORMALIZED (deduped) variant index +// at positions 0/1/2. dedup_match pins that a deduped (i32|i32|str) constructs + +// matches its str variant in both stages. Migrated from +// test/wcc/989_tagnorm_run.c (value rows). dedup_match: the old C driver's comment +// claimed its str tag-NUMBER diverged (cs $1 vs ww $2) — that divergence tag is +// STALE at HEAD: byte-id verified cs==ww AND T1 value-correct (the @test runs and +// asserts r==7), so it migrates as a NORMAL byte-id @test row, not a runonly. + +package tagnorm_test; + +type tn_p = (*u8 | void); +type tn_q = (i32 | never); +type tn_d = (i32 | i32); +type tn_u3str = (i32 | i32 | str); +type tn_uvoid = (i32 | i32 | void); +type tn_vpos0 = (void | i32); +type tn_vpos2 = (i32 | str | void); + +fn f_void_dedup() tn_uvoid = { return; }; +fn f_void_pos0() tn_vpos0 = { return; }; +fn f_void_pos2() tn_vpos2 = { return; }; + +@test fn nullable_size() void = { + assert(size(tn_p): int == 8); +}; + +@test fn never_size() void = { + assert(size(tn_q): int == 4); +}; + +@test fn dup_size() void = { + assert(size(tn_d): int == 4); +}; + +@test fn never_align() void = { + assert(align(tn_q): int == 4); +}; + +@test fn dedup_match() void = { + let v: tn_u3str = "hi"; + let r: i64 = 0; + match (v) { + case let s: str => { r = 7; }; + case let x: i32 => { r = 1; }; + }; + assert(r == 7); +}; + +@test fn void_dedup_ret() void = { + let v: tn_uvoid = f_void_dedup(); + let r: i64 = 0; + match (v) { + case void => { r = 5; }; + case let x: i32 => { r = 9; }; + }; + assert(r == 5); +}; + +@test fn void_pos0_ret() void = { + let v: tn_vpos0 = f_void_pos0(); + let r: i64 = 0; + match (v) { + case void => { r = 5; }; + case let x: i32 => { r = 9; }; + }; + assert(r == 5); +}; + +@test fn void_pos2_ret() void = { + let v: tn_vpos2 = f_void_pos2(); + let r: i64 = 0; + match (v) { + case void => { r = 5; }; + case let s: str => { r = 8; }; + case let x: i32 => { r = 9; }; + }; + assert(r == 5); +}; diff --git a/test/lang/tryprop_tag_remap_test.ww b/test/lang/tryprop_tag_remap_test.ww new file mode 100644 index 00000000..ed6dee7e --- /dev/null +++ b/test/lang/tryprop_tag_remap_test.ww @@ -0,0 +1,82 @@ +// tryprop_tag_remap_test — the `?` try-operator must remap variant tags when the +// propagated error union's variant ORDER differs between callee and caller +// ((i32|e1|e2) propagated through (i32|e2|e1)), and pass multiword (str) error +// payloads intact. Migrated from test/wcc/925_tryprop_tag_remap_run.c (value rows). + +package tryprop_tag_remap_test; + +type e1 = !void; +type e2 = !void; +type emsg = !str; + +fn g(which: i32) (i32 | e1 | e2) = { + if (which == 1) { return e1{}; }; + if (which == 2) { return e2{}; }; + return 100; +}; + +// caller variant order (i32|e2|e1) differs from g's (i32|e1|e2): the remap fires. +fn f_diff(which: i32) (i32 | e2 | e1) = { + let v: i32 = g(which)?; + return v + 1; +}; + +// caller order matches g's: no remap (control). +fn f_same(which: i32) (i32 | e1 | e2) = { + let v: i32 = g(which)?; + return v + 1; +}; + +fn g_mw(which: i32) (i32 | e1 | emsg) = { + if (which == 1) { return e1{}; }; + if (which == 2) { let m: emsg = "AB": emsg; return m; }; + return 100; +}; +fn f_mw(which: i32) (i32 | emsg | e1) = { + let v: i32 = g_mw(which)?; + return v + 1; +}; + +@test fn diff_order_e1() void = { + match (f_diff(1)) { + case let v: i32 => { assert(false); }; + case e2 => { assert(false); }; + case e1 => { }; + }; +}; + +@test fn diff_order_e2() void = { + match (f_diff(2)) { + case let v: i32 => { assert(false); }; + case e2 => { }; + case e1 => { assert(false); }; + }; +}; + +@test fn diff_order_success() void = { + match (f_diff(0)) { + case let v: i32 => { assert(v == 101); }; + case e2 => { assert(false); }; + case e1 => { assert(false); }; + }; +}; + +@test fn same_order() void = { + match (f_same(1)) { + case let v: i32 => { assert(false); }; + case e1 => { }; + case e2 => { assert(false); }; + }; +}; + +@test fn multiword() void = { + match (f_mw(2)) { + case let v: i32 => { assert(false); }; + case let s: emsg => { + assert(s.len == 2); + assert(s[0] == 65u8); + assert(s[1] == 66u8); + }; + case e1 => { assert(false); }; + }; +}; diff --git a/test/lang/variant_chain_b95_runonly_test.ww b/test/lang/variant_chain_b95_runonly_test.ww new file mode 100644 index 00000000..7012067d --- /dev/null +++ b/test/lang/variant_chain_b95_runonly_test.ww @@ -0,0 +1,38 @@ +// variant_chain_b95_runonly_test — the two #95 CALL-source chain rows whose mk() +// frame carries the pre-existing #81 uninit-local zero-fill asm divergence: the +// `is` result is cs==ww runtime-correct, but the surrounding frame byte-diverges +// cs≠ww (probed at HEAD: real, both standalone and @test forms). So these migrate +// VALUE-ONLY (_runonly, byte-id-EXCLUDED) per the divergence carve; the byte-id +// net is waived pending #81. Migrated from test/wcc/944_variant_chain_b95_run.c +// (K_RUN_NOID rows, #5-C5). + +package variant_chain_b95_runonly_test; + +type cc1_base = struct { a: int, b: int }; +type cc1_al0 = cc1_base; + +type cc2_base = struct { a: int, b: int }; +type cc2_al0 = cc2_base; +type cc2_ali = cc2_al0; + +fn cc1_mk() cc1_base = { + let s: cc1_base; + s.a = 4; s.b = 9; + return s; +}; + +fn cc2_mk() cc2_base = { + let s: cc2_base; + s.a = 4; s.b = 9; + return s; +}; + +@test fn chain_call_bound81() void = { + let v: (void | cc1_al0) = cc1_mk(); + assert(v is cc1_al0); +}; + +@test fn chain_call2_bound81() void = { + let v: (void | cc2_ali) = cc2_mk(); + assert(v is cc2_ali); +}; diff --git a/test/lang/variant_chain_b95_test.ww b/test/lang/variant_chain_b95_test.ww new file mode 100644 index 00000000..d24c0091 --- /dev/null +++ b/test/lang/variant_chain_b95_test.ww @@ -0,0 +1,145 @@ +// variant_chain_b95_test — #95: widening a source whose TYPE reaches a union +// variant through an alias CHAIN (or by structural fallback) must tag the right +// variant. Pins 1/2/deep-level chain membership, exact-precedence (ali|ali2), +// nominal (str|linerr) regression, bare-pointer source, and unrelated-struct +// structural fallback. Migrated from test/wcc/944_variant_chain_b95_run.c +// (K_RUN rows: build+run cs==ww + asm byte-id). The K_BUILDERR reject rows are +// runww //ww:error carriers (test/wcc/data/vchain_*); the cstage-runs/ww-rejects +// #277 row + the #81 call-source byte-id-waived rows are carried separately. +// Types are per-row-prefixed because the C rows reused names with different shapes. + +package variant_chain_b95_test; + +type c1_base = struct { a: int, b: int }; +type c1_al0 = c1_base; + +type c2_base = struct { a: int, b: int }; +type c2_al0 = c2_base; +type c2_ali = c2_al0; + +type cds_base = struct { a: int, b: int }; +type cds_ali = cds_base; +type cds_ali2 = cds_ali; + +type cdv_base = struct { a: int, b: int }; +type cdv_ali = cdv_base; +type cdv_ali2 = cdv_ali; + +type casa_base = struct { a: int, b: int }; +type casa_ali = casa_base; +type casa_ali2 = casa_ali; + +type casb_base = struct { a: int, b: int }; +type casb_ali = casb_base; +type casb_ali2 = casb_ali; + +type ns_linerr = str; +type ne_linerr = str; + +type ec_base = struct { a: int, b: int }; +type ec_al0 = ec_base; +type ec_ali = ec_al0; + +type bc_vt = struct { a: int }; +type bc_stream = *bc_vt; + +type b2_vt = struct { a: int }; +type b2_st0 = *b2_vt; +type b2_stream = b2_st0; + +type us_ta = struct { a: int, b: int }; +type us_tb = struct { a: int, b: int }; + +@test fn chain_1lvl_i() void = { + let s: c1_base; + s.a = 4; s.b = 9; + let v: (void | c1_al0) = s; + assert(v is c1_al0); +}; + +@test fn chain_2lvl_i() void = { + let s: c2_base; + s.a = 4; s.b = 9; + let v: (void | c2_ali) = s; + assert(v is c2_ali); +}; + +@test fn chain_deep_src() void = { + let s: cds_ali2; + s.a = 4; s.b = 9; + let v: (void | cds_ali) = s; + assert(v is cds_ali); +}; + +@test fn chain_deep_var() void = { + let s: cdv_base; + s.a = 4; s.b = 9; + let v: (void | cdv_ali2) = s; + assert(v is cdv_ali2); +}; + +@test fn chain_amb_srcA() void = { + let s: casa_ali; + s.a = 4; s.b = 9; + let v: (casa_ali | casa_ali2) = s; + assert(v is casa_ali); + assert(!(v is casa_ali2)); +}; + +@test fn chain_amb_srcB() void = { + let s: casb_ali2; + s.a = 4; s.b = 9; + let v: (casb_ali | casb_ali2) = s; + assert(v is casb_ali2); + assert(!(v is casb_ali)); +}; + +@test fn nom_str() void = { + let s: str = "ok"; + let v: (str | ns_linerr) = s; + assert(v is str); + assert(!(v is ns_linerr)); +}; + +@test fn nom_err() void = { + let e: ne_linerr = "bad"; + let v: (str | ne_linerr) = e; + assert(v is ne_linerr); + assert(!(v is str)); +}; + +@test fn exact_ctl() void = { + let s: ec_ali; + s.a = 4; s.b = 9; + let v: (void | ec_ali) = s; + assert(v is ec_ali); +}; + +@test fn bare_ctl() void = { + let x: bc_vt; + x.a = 7; + let v: (void | bc_stream) = &x; + assert(v is bc_stream); + match (v) { + case let s: bc_stream => { assert(s.a == 7); }; + case void => { assert(false); }; + }; +}; + +@test fn bare_2lvl() void = { + let x: b2_vt; + x.a = 7; + let v: (void | b2_stream) = &x; + assert(v is b2_stream); + match (v) { + case let s: b2_stream => { assert(s.a == 7); }; + case void => { assert(false); }; + }; +}; + +@test fn unrel_struct() void = { + let s: us_ta; + s.a = 4; s.b = 9; + let v: (void | us_tb) = s; + assert(v is us_tb); +}; diff --git a/test/lang/variant_typekey_test.ww b/test/lang/variant_typekey_test.ww new file mode 100644 index 00000000..7e0aa79e --- /dev/null +++ b/test/lang/variant_typekey_test.ww @@ -0,0 +1,46 @@ +// variant_typekey_test — nominal-type-key variant discrimination. A call- +// returned value with no surface name (str or i32) widened into (alias | base) +// must tag the structural (base) variant, not the leading alias. Both the str +// axis ((linerr|str), linerr=!str) and the scalar axis ((ec|i32), ec=!i32). +// Migrated from test/wcc/931_variant_typekey_run.c (value rows, cs==ww). + +package variant_typekey_test; + +type linerr = !str; +type ec = !i32; + +fn getstr() str = { return "hi"; }; +fn produce_str(k: i32) (linerr | str) = { + if (k == 0) { return getstr(); }; + let e: linerr = "boom"; + return e; +}; +fn classify_str(k: i32) i32 = { + match (produce_str(k)) { + case let er: linerr => return 1; + case let v: str => return 2; + }; +}; + +fn getint() i32 = { return 7; }; +fn produce_int(k: i32) (ec | i32) = { + if (k == 0) { return getint(); }; + let e: ec = 9; + return e; +}; +fn classify_int(k: i32) i32 = { + match (produce_int(k)) { + case let er: ec => return 1; + case let v: i32 => return 2; + }; +}; + +@test fn linerr_str_alias_first_callret() void = { + assert(classify_str(0) == 2); + assert(classify_str(1) == 1); +}; + +@test fn errint_int_alias_first_callret() void = { + assert(classify_int(0) == 2); + assert(classify_int(1) == 1); +}; diff --git a/test/lang/void_error_singleton_test.ww b/test/lang/void_error_singleton_test.ww new file mode 100644 index 00000000..f92cacc3 --- /dev/null +++ b/test/lang/void_error_singleton_test.ww @@ -0,0 +1,75 @@ +// void_error_singleton_test — a `!void` error singleton (tag-only) across +// return / let-init / assign / call-arg positions, plus a wide (str) success +// payload exercising CX/R8 zeroing on the tag-only return. Migrated from +// test/wcc/949_void_error_singleton_run.c (value rows; C driver asserted cs==ww .s). + +package void_error_singleton_test; + +type too_long = !void; + +fn f_ret_singleton() (i64 | too_long) = { + return too_long; +}; + +fn f_ret_success() (i64 | too_long) = { + return 42i64; +}; + +fn use_callarg(r: (i64 | too_long)) i32 = { + match (r) { + case let v: i64 => return 1; + case too_long => return 0; + }; + return 2; +}; + +fn f_wide(ok: bool) (str | too_long) = { + if (ok) { return "hello"; }; + return too_long; +}; + +@test fn return_singleton() void = { + match (f_ret_singleton()) { + case let v: i64 => { assert(false); }; + case too_long => { }; + }; +}; + +@test fn return_success() void = { + match (f_ret_success()) { + case let v: i64 => { assert(v == 42); }; + case too_long => { assert(false); }; + }; +}; + +@test fn letinit_singleton() void = { + let e: (i64 | too_long) = too_long; + match (e) { + case let v: i64 => { assert(false); }; + case too_long => { }; + }; +}; + +@test fn assign_singleton() void = { + let e: (i64 | too_long) = 5i64; + e = too_long; + match (e) { + case let v: i64 => { assert(false); }; + case too_long => { }; + }; +}; + +@test fn callarg_singleton() void = { + assert(use_callarg(too_long) == 0); +}; + +@test fn return_singleton_wide() void = { + match (f_wide(false)) { + case let s: str => { assert(false); }; + case too_long => { }; + }; + match (f_wide(true)) { + case let s: str => { assert(s.len == 5); }; + case too_long => { assert(false); }; + }; +}; diff --git a/test/wcc/924_tagged_call_arg_run.c b/test/wcc/924_tagged_call_arg_run.c deleted file mode 100644 index 5f0ca72e..00000000 --- a/test/wcc/924_tagged_call_arg_run.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * 924_tagged_call_arg_run — wwstage caller must not drop the DX/CX/R8 - * payload words when passing a tagged-union arg sourced from a fn - * call that returns the same tagged union (task #21). - * - * Pre-fix the wwstage call-arg-emit dispatch in pushargsrev only - * recognized N_IDENT as an already-tagged source. For arg.kind == - * N_CALL whose callee returns the param's tagged type, the widening - * path treated AX (the returned tag word) as a concrete-variant - * payload, then clobbered AX with widentag for the tag-push slot. - * The DX/CX/R8 value-words from the tagged-return ABI were silently - * dropped — the callee read its second arg-reg (SI / DX / etc.) as - * 0 or as the original tag. - * - * Cstage already had the right shape: a `type_eq(p->type, at)` guard - * at cmd/w6c/cgen.c:4216-4221 sets widen[i]=0, then the natural-push - * branch at 4373-4387 pushes R8/CX/DX/AX high→low. Fix aligns - * wwstage DOWN to that shape (rule 10): - * 1. pushargsrev's aistagged guard (cgenutil.ww) now also fires - * when taggedcallslot(arg) == slotsize(ptype), matching cstage. - * 2. The natural-push fallthrough adds a taggedcallslot arm that - * pushes R8/CX/DX/AX high→low by slot size. - * 3. cgcall's per-arg pop-count uses taggedcallslot too so the - * next arg's POPQ doesn't land on residual tagged words. - * - * Rows pin: - * - 4-variant (rune | done | more | invalid) CALL-source: the - * original utf8 iterator shape from worker-encoding's probe. - * Two-word tagged (16B slot), CALL→CALL composition. - * - IDENT-source guard: a parallel test that was already correct - * pre-fix, pinning that the IDENT fast-path stays byte-identical - * to cstage and isn't perturbed by the fix. - * - 2-variant `(*u8 | oserror)` sanity: narrower 16B slot variant - * to ensure the natural-push arm doesn't regress smaller-slot - * shapes. - * - Multi-arg sequence: tagged-CALL arg followed by a scalar arg, - * pinning that the pop-count fix keeps subsequent args in the - * right register class. - */ -#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; }; - -static const struct row rows[] = { - { "4variant_call_source_rune", - "type done = !void; type more = !void; type invalid = !void;\n" - "fn yield_rune() (rune | done | more | invalid) = {\n" - " return 65u32: rune;\n" - "};\n" - "fn dispatch(v: (rune | done | more | invalid)) i32 = {\n" - " match (v) {\n" - " case let r: rune => return r: i32;\n" - " case let d: done => return -1;\n" - " case let m: more => return -2;\n" - " case let e: invalid => return -3;\n" - " };\n" - " return -99;\n" - "};\n" - "export fn main() i32 = {\n" - " if (dispatch(yield_rune()) != 65) { return 11; };\n" - " return 0;\n" - "};\n", - 0 }, - { "4variant_ident_source_rune", - "type done = !void; type more = !void; type invalid = !void;\n" - "fn dispatch(v: (rune | done | more | invalid)) i32 = {\n" - " match (v) {\n" - " case let r: rune => return r: i32;\n" - " case let d: done => return -1;\n" - " case let m: more => return -2;\n" - " case let e: invalid => return -3;\n" - " };\n" - " return -99;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: (rune | done | more | invalid) = 65u32: rune;\n" - " if (dispatch(v) != 65) { return 11; };\n" - " return 0;\n" - "};\n", - 0 }, - { "2variant_call_source_ptr", - "type oserror = !i32;\n" - "fn yield_ptr() (*u8 | oserror) = {\n" - " let p: *u8 = nil;\n" - " return p;\n" - "};\n" - "fn dispatch(v: (*u8 | oserror)) i32 = {\n" - " match (v) {\n" - " case let p: *u8 => return 7;\n" - " case let e: oserror => return e: i32;\n" - " };\n" - " return -99;\n" - "};\n" - "export fn main() i32 = {\n" - " if (dispatch(yield_ptr()) != 7) { return 11; };\n" - " return 0;\n" - "};\n", - 0 }, - { "2variant_call_source_err", - "type oserror = !i32;\n" - "fn yield_err() (*u8 | oserror) = {\n" - " let e: oserror = 42i32: oserror;\n" - " return e;\n" - "};\n" - "fn dispatch(v: (*u8 | oserror)) i32 = {\n" - " match (v) {\n" - " case let p: *u8 => return 7;\n" - " case let e: oserror => return e: i32;\n" - " };\n" - " return -99;\n" - "};\n" - "export fn main() i32 = {\n" - " if (dispatch(yield_err()) != 42) { return 11; };\n" - " return 0;\n" - "};\n", - 0 }, - /* Multi-arg shape: tagged-CALL followed by a scalar i32 arg. - * Pre-fix the pop-count would be wrong (1 word for arg0 instead - * of 2), leaving arg1's scalar to pick up the residual tag word - * off the stack and routing the real scalar into DX. Post-fix - * the per-arg word count matches the push. */ - { "tagged_call_then_scalar", - "type oserror = !i32;\n" - "fn yield_ptr() (*u8 | oserror) = {\n" - " let p: *u8 = nil;\n" - " return p;\n" - "};\n" - "fn use2(v: (*u8 | oserror), k: i32) i32 = {\n" - " match (v) {\n" - " case let p: *u8 => return k + 1;\n" - " case let e: oserror => return k - 1;\n" - " };\n" - " return -99;\n" - "};\n" - "export fn main() i32 = {\n" - " if (use2(yield_ptr(), 41) != 42) { return 11; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/tca_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tca_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tca_%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", - driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - runwait(rmcmd); - return -1; - } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; -} - -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, "tagged_call_arg_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "tagged_call_arg_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - if (fail) { - fprintf(stderr, - "tagged_call_arg_run: %d/%d fixtures failed\n", fail, total); - return 1; - } - printf("tagged_call_arg_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/925_nested_union_widen_run.c b/test/wcc/925_nested_union_widen_run.c deleted file mode 100644 index dab74b38..00000000 --- a/test/wcc/925_nested_union_widen_run.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * 925_nested_union_widen_run — widening a NAMED multi-variant union - * value into an ENCLOSING union must tag the OUTER variant correctly - * and lay the inner tagged value out as the payload at slot+8 (project - * #218). - * - * Gate-blind hazard: the outer-widen of a NAMED multi-variant union - * (`inner`) into `(size | inner)` took the tagged-SUBSET path — it - * byte-copied the inner value at slot+0 and remapped its SUB-variants, - * collapsing every inner sub-variant onto outer tag 0. The match - * EXTRACT side already read the inner value from slot+8 (nested layout), - * so store and extract disagreed and the outer arm mis-selected. The - * byte-id (990-997) and cs==ww asm gates both passed because the - * bootstrap never widens a multi-variant union into an enclosing one; - * the #94 io eFinal error path (`let e: io.error = ; return e`) - * is the first to exercise it. cstage cg_widen_tagged_store + - * cg_variant_match (cmd/w6c/cgen.c) and the wwstage twin - * (selfhost/cmd/wcc/cgenutil.ww cgwidentaggedstorebp + cgvariantmatch) - * now detect the nested-variant case (the source's whole tagged type - * matches one outer variant — recovered via a structural fallback after - * the nominal-lossy collapse, project tinfo_lossy_nominal) and store - * the inner value at slot+8 with the outer tag at slot+0. - * - * Two assertions per RUN row: - * - RUNTIME: build with both `ww` (cstage) and `ww_ww` (wwstage), - * run, compare the exit code. This is what was wrong pre-fix. - * - ASM BYTE-ID: compile through `w6c` and `w6c_ww` and require - * byte-identical .s (rule-10). - * - * ERR rows assert a COMPILE-ERROR on both stages (the drew collision - * guard): when ≥2 outer variants are structurally identical, the - * structural fallback cannot disambiguate them once nominal identity - * is lost (#199b/B-full), so the compiler must STOP rather than silently - * mis-tag. The over-match is unreachable under today's nominal-lossy - * model but INVERTS when #199b lands — the guard pins the invariant now. - * - * Rows: - * 1. outer_arm_select — let-widen `e: inner` into `(size | inner)`; - * match selects the `inner` arm (the bug returned the `size` arm). - * 2. destructure_return — `return e` widens through the tagged-return - * ABI; the caller destructures the inner i32 payload (5) — proves - * the payload survives the OUTER round-trip (the rule-7 gate: if - * this fails it is the deep wrapped-slot-layout, not this fix). - * 3. destructure_let — let-widen + nested match unwraps the inner i32 - * payload (7) — the payload survives a non-return widen too. - * 4. single_variant — scalar widen into `(i32 | bool)`; the non-nested - * single-variant path is unperturbed (control + byte-id witness). - * 5. collision_guard — two structurally-identical NAMED unions as - * sibling variants `(a | b)`; widening must COMPILE-ERROR (the - * guard), not silently pick one. - */ -#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 != 0: the source must FAIL to compile on both stages. */ -struct row { const char *label; const char *src; int want; int experr; }; - -static const struct row rows[] = { - { "outer_arm_select", - "type inner = !(i32|bool);\n" - "export fn main() i32 = {\n" - " let e: inner = (true: inner);\n" - " let r: (size | inner) = e;\n" - " match (r) {\n" - " case let n: size => return 50;\n" - " case let x: inner => return 0;\n" - " };\n" - "};\n", - 0, 0 }, - { "destructure_return", - "type inner = !(i32|bool);\n" - "fn g() (size | inner) = {\n" - " let e: inner = (5i32: inner);\n" - " return e;\n" - "};\n" - "export fn main() i32 = {\n" - " match (g()) {\n" - " case let s: size => return 90;\n" - " case let x: inner => match (x) {\n" - " case let i: i32 => return i;\n" - " case let b: bool => return 99;\n" - " };\n" - " };\n" - "};\n", - 5, 0 }, - { "destructure_let", - "type inner = !(i32|bool);\n" - "export fn main() i32 = {\n" - " let e: inner = (7i32: inner);\n" - " let r: (size | inner) = e;\n" - " match (r) {\n" - " case let n: size => return 50;\n" - " case let x: inner => match (x) {\n" - " case let i: i32 => return i;\n" - " case let b: bool => return 88;\n" - " };\n" - " };\n" - "};\n", - 7, 0 }, - { "single_variant", - "export fn main() i32 = {\n" - " let r: (i32 | bool) = 7i32;\n" - " match (r) {\n" - " case let n: i32 => return n;\n" - " case let b: bool => return 88;\n" - " };\n" - "};\n", - 7, 0 }, - { "collision_guard", - "type a = !(i32|bool);\n" - "type b = !(i32|bool);\n" - "export fn main() i32 = {\n" - " let x: a = (1i32: a);\n" - " let r: (a | b) = x;\n" - " match (r) {\n" - " case let p: a => return 1;\n" - " case let q: b => return 2;\n" - " };\n" - "};\n", - 0, 1 }, -}; - -/* Write r->src to /.ww; returns 0 on success. */ -static int -write_src(const char *dir, const char *base, const struct row *r, char *out, - size_t outsz) -{ - snprintf(out, outsz, "%s/%s.ww", dir, base); - FILE *f = fopen(out, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - return 0; -} - -/* Build src with `driver build` inside its own subdir, run the binary, - * return its exit code (or -1 on a build/exec failure). */ -static int -build_run(const char *driver, const char *src, const char *workdir) -{ - char cmd[8192]; - snprintf(cmd, sizeof cmd, "cd %s && %s build %s > /dev/null 2>&1", - workdir, driver, src); - if (runwait(cmd) != 0) return -1; - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[1024]; - snprintf(outbin, sizeof outbin, "%s/%s", workdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - return runwait(outbin); -} - -static int -files_equal(const char *a, const char *b) -{ - FILE *fa = fopen(a, "rb"); - FILE *fb = fopen(b, "rb"); - if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; } - int ca, cb, eq = 1; - do { - ca = fgetc(fa); - cb = fgetc(fb); - if (ca != cb) { eq = 0; break; } - } while (ca != EOF); - fclose(fa); - fclose(fb); - return eq; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640], wdrv[640], cw6[640], ww6[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - snprintf(cw6, sizeof cw6, "%s/w6c", bin); - snprintf(ww6, sizeof ww6, "%s/w6c_ww", bin); - - int have_ww = (access(wdrv, X_OK) == 0); - int have_w6cww = (access(ww6, X_OK) == 0); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int i = 0; i < n; i++) { - const struct row *r = &rows[i]; - - char dir[] = "/tmp/nuw.XXXXXX"; - if (mkdtemp(dir) == NULL) { - fprintf(stderr, "row[%s]: mkdtemp failed\n", r->label); - fail++; total++; - continue; - } - - char src[1024]; - if (write_src(dir, "p", r, src, sizeof src) != 0) { - fprintf(stderr, "row[%s]: write src failed\n", r->label); - fail++; total++; - goto cleanup; - } - - if (r->experr) { - /* Collision guard: w6c (and w6c_ww) must REFUSE to - * emit. A successful emit is the silent mis-tag the - * guard exists to stop. */ - char css[1024], wss[1024], cmd[8192]; - snprintf(css, sizeof css, "%s/cs.s", dir); - snprintf(wss, sizeof wss, "%s/ww.s", dir); - snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1", - cw6, css, src); - total++; - if (runwait(cmd) == 0) { - fprintf(stderr, - "row[%s][cstage]: emit SUCCEEDED, " - "want compile-error (#218/#199b)\n", - r->label); - fail++; - } - if (have_w6cww) { - snprintf(cmd, sizeof cmd, - "%s -o %s %s > /dev/null 2>&1", - ww6, wss, src); - total++; - if (runwait(cmd) == 0) { - fprintf(stderr, - "row[%s][wwstage]: emit SUCCEEDED, " - "want compile-error (#218/#199b)\n", - r->label); - fail++; - } - } - goto cleanup; - } - - /* ASM byte-id: w6c vs w6c_ww .s must be identical. */ - if (have_w6cww) { - char css[1024], wss[1024], cmd[8192]; - snprintf(css, sizeof css, "%s/cs.s", dir); - snprintf(wss, sizeof wss, "%s/ww.s", dir); - snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1", - cw6, css, src); - int rc1 = runwait(cmd); - snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1", - ww6, wss, src); - int rc2 = runwait(cmd); - total++; - if (rc1 != 0 || rc2 != 0) { - fprintf(stderr, - "row[%s]: w6c/w6c_ww emit failed (%d/%d)\n", - r->label, rc1, rc2); - fail++; - } else if (files_equal(css, wss) != 1) { - fprintf(stderr, - "row[%s]: cs.s != ww.s (rule-10 break)\n", - r->label); - fail++; - } - } - - /* RUNTIME: cstage. */ - { - char wk[1024]; - snprintf(wk, sizeof wk, "%s/cs", dir); - mkdir(wk, 0755); - int got = build_run(cdrv, src, wk); - total++; - if (got != r->want) { - fprintf(stderr, - "row[%s][cstage]: exit=%d want=%d\n", - r->label, got, r->want); - fail++; - } - } - - /* RUNTIME: wwstage (the second half of rule-10). */ - if (have_ww) { - char wk[1024]; - snprintf(wk, sizeof wk, "%s/ww", dir); - mkdir(wk, 0755); - int got = build_run(wdrv, src, wk); - total++; - if (got != r->want) { - fprintf(stderr, - "row[%s][wwstage]: exit=%d want=%d\n", - r->label, got, r->want); - fail++; - } - } - -cleanup: - { - char rm[1100]; - snprintf(rm, sizeof rm, "rm -rf %s", dir); - runwait(rm); - } - } - - if (fail) { - fprintf(stderr, - "nested_union_widen_run: %d/%d checks failed\n", fail, total); - return 1; - } - printf("nested_union_widen_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/925_tryprop_tag_remap_run.c b/test/wcc/925_tryprop_tag_remap_run.c deleted file mode 100644 index 5b54c5cc..00000000 --- a/test/wcc/925_tryprop_tag_remap_run.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * 925_tryprop_tag_remap_run — the `?` try operator must remap the - * operand union's error-variant tag into the ENCLOSING fn return - * union's variant order before propagating (project #173). - * - * Gate-blind hazard: when the `?` operand union and the enclosing fn - * return union differ in variant ORDER, the raw operand tag names the - * WRONG variant in the return union. Pre-fix wwstage cgtryprop did NO - * remap (selfhost/cmd/wcc/cgenexpr.ww) and propagated the raw tag, so - * the caller saw the wrong error variant at runtime — while the - * byte-id (990-997) and cs==ww asm gates both passed, because the - * bootstrap only ever tries SAME-order unions. cstage already remapped - * (cmd/w6c/cgen.c:6161-6184); the fix ports that loop into wwstage. - * - * Two assertions per row: - * - RUNTIME: build with both `ww` (cstage) and `ww_ww` (wwstage), - * run, compare the exit code (= which variant the caller matched). - * This is what was wrong pre-fix. - * - ASM BYTE-ID: compile the same source through `w6c` and `w6c_ww` - * and require byte-identical .s. Proves rule-10 (symmetric stages) - * holds AFTER the fix on differing-order too, and that same-order - * emits no remap (zero delta). - * - * Rows: - * 1. diff_order_e1 — g→e1 on a branched callee; f's union swaps the - * error order; caller asserts the e1 arm. Pre-fix wwstage = 2. - * 2. diff_order_e2 — g→e2; caller asserts the e2 arm. Proves BOTH - * error variants remap (not just the first). - * 3. diff_order_success — g→value; caller asserts the i32 success - * unwrap survives unperturbed by the remap. - * 4. same_order — f's union same order as g; the only shape the - * bootstrap exercises; byte-id witness that the remap is inert. - * 5. multiword — differing-order propagation of a MULTI-WORD `!str` - * error (payload in DX/CX/R8). Caller asserts the PAYLOAD BYTES - * ("AB") survive, not just the tag — proves the remap touches - * only AX and the payload regs ride the RET. - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - { "diff_order_e1", - "type e1 = !void;\n" - "type e2 = !void;\n" - "fn g(which: i32) (i32 | e1 | e2) = {\n" - " if (which == 1) { return e1{}; };\n" - " if (which == 2) { return e2{}; };\n" - " return 100;\n" - "};\n" - "fn f(which: i32) (i32 | e2 | e1) = {\n" - " let v: i32 = g(which)?;\n" - " return v + 1;\n" - "};\n" - "export fn main() i32 = {\n" - " match (f(1)) {\n" - " case let v: i32 => return 50;\n" - " case e2 => return 2;\n" - " case e1 => return 1;\n" - " };\n" - " return 99;\n" - "};\n", - 1 }, - { "diff_order_e2", - "type e1 = !void;\n" - "type e2 = !void;\n" - "fn g(which: i32) (i32 | e1 | e2) = {\n" - " if (which == 1) { return e1{}; };\n" - " if (which == 2) { return e2{}; };\n" - " return 100;\n" - "};\n" - "fn f(which: i32) (i32 | e2 | e1) = {\n" - " let v: i32 = g(which)?;\n" - " return v + 1;\n" - "};\n" - "export fn main() i32 = {\n" - " match (f(2)) {\n" - " case let v: i32 => return 50;\n" - " case e2 => return 2;\n" - " case e1 => return 1;\n" - " };\n" - " return 99;\n" - "};\n", - 2 }, - { "diff_order_success", - "type e1 = !void;\n" - "type e2 = !void;\n" - "fn g(which: i32) (i32 | e1 | e2) = {\n" - " if (which == 1) { return e1{}; };\n" - " if (which == 2) { return e2{}; };\n" - " return 100;\n" - "};\n" - "fn f(which: i32) (i32 | e2 | e1) = {\n" - " let v: i32 = g(which)?;\n" - " return v + 1;\n" - "};\n" - "export fn main() i32 = {\n" - " match (f(0)) {\n" - " case let v: i32 => { if (v != 101) { return 40; }; return 7; };\n" - " case e2 => return 2;\n" - " case e1 => return 1;\n" - " };\n" - " return 99;\n" - "};\n", - 7 }, - { "same_order", - "type e1 = !void;\n" - "type e2 = !void;\n" - "fn g(which: i32) (i32 | e1 | e2) = {\n" - " if (which == 1) { return e1{}; };\n" - " if (which == 2) { return e2{}; };\n" - " return 100;\n" - "};\n" - "fn f(which: i32) (i32 | e1 | e2) = {\n" - " let v: i32 = g(which)?;\n" - " return v + 1;\n" - "};\n" - "export fn main() i32 = {\n" - " match (f(1)) {\n" - " case let v: i32 => return 50;\n" - " case e1 => return 1;\n" - " case e2 => return 2;\n" - " };\n" - " return 99;\n" - "};\n", - 1 }, - { "multiword", - "type e1 = !void;\n" - "type emsg = !str;\n" - "fn g(which: i32) (i32 | e1 | emsg) = {\n" - " if (which == 1) { return e1{}; };\n" - " if (which == 2) { let m: emsg = \"AB\": emsg; return m; };\n" - " return 100;\n" - "};\n" - "fn f(which: i32) (i32 | emsg | e1) = {\n" - " let v: i32 = g(which)?;\n" - " return v + 1;\n" - "};\n" - "export fn main() i32 = {\n" - " match (f(2)) {\n" - " case let v: i32 => return 50;\n" - " case let s: emsg => {\n" - " if (s.len != 2) { return 30; };\n" - " if (s[0] != 65u8) { return 31; };\n" - " if (s[1] != 66u8) { return 32; };\n" - " return 7;\n" - " };\n" - " case e1 => return 1;\n" - " };\n" - " return 99;\n" - "};\n", - 7 }, -}; - -/* Write r->src to /.ww; returns 0 on success. */ -static int -write_src(const char *dir, const char *base, const struct row *r, char *out, - size_t outsz) -{ - snprintf(out, outsz, "%s/%s.ww", dir, base); - FILE *f = fopen(out, "wb"); - if (!f) return -1; - fputs(r->src, f); - fclose(f); - return 0; -} - -/* Build src with `driver build` inside its own subdir, run the binary, - * return its exit code (or -1 on a build/exec failure). */ -static int -build_run(const char *driver, const char *src, const char *workdir) -{ - char cmd[8192]; - snprintf(cmd, sizeof cmd, "cd %s && %s build %s > /dev/null 2>&1", - workdir, driver, src); - if (runwait(cmd) != 0) return -1; - - const char *base = strrchr(src, '/'); - base = base ? base + 1 : src; - char outbin[1024]; - snprintf(outbin, sizeof outbin, "%s/%s", workdir, base); - char *dot = strrchr(outbin, '.'); - if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; - return runwait(outbin); -} - -static int -files_equal(const char *a, const char *b) -{ - FILE *fa = fopen(a, "rb"); - FILE *fb = fopen(b, "rb"); - if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; } - int ca, cb, eq = 1; - do { - ca = fgetc(fa); - cb = fgetc(fb); - if (ca != cb) { eq = 0; break; } - } while (ca != EOF); - fclose(fa); - fclose(fb); - return eq; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - char absbin[512]; - if (bin[0] != '/') { - char cwd[256]; - if (getcwd(cwd, sizeof cwd) == NULL) return 1; - snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); - bin = absbin; - } - - char cdrv[640], wdrv[640], cw6[640], ww6[640]; - snprintf(cdrv, sizeof cdrv, "%s/ww", bin); - snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); - snprintf(cw6, sizeof cw6, "%s/w6c", bin); - snprintf(ww6, sizeof ww6, "%s/w6c_ww", bin); - - int have_ww = (access(wdrv, X_OK) == 0); - int have_w6cww = (access(ww6, X_OK) == 0); - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - - for (int i = 0; i < n; i++) { - const struct row *r = &rows[i]; - - char dir[] = "/tmp/tprm.XXXXXX"; - if (mkdtemp(dir) == NULL) { - fprintf(stderr, "row[%s]: mkdtemp failed\n", r->label); - fail++; total++; - continue; - } - - char src[1024]; - if (write_src(dir, "p", r, src, sizeof src) != 0) { - fprintf(stderr, "row[%s]: write src failed\n", r->label); - fail++; total++; - goto cleanup; - } - - /* ASM byte-id: w6c vs w6c_ww .s must be identical. */ - if (have_w6cww) { - char css[1024], wss[1024], cmd[8192]; - snprintf(css, sizeof css, "%s/cs.s", dir); - snprintf(wss, sizeof wss, "%s/ww.s", dir); - snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1", - cw6, css, src); - int rc1 = runwait(cmd); - snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1", - ww6, wss, src); - int rc2 = runwait(cmd); - total++; - if (rc1 != 0 || rc2 != 0) { - fprintf(stderr, - "row[%s]: w6c/w6c_ww emit failed (%d/%d)\n", - r->label, rc1, rc2); - fail++; - } else if (files_equal(css, wss) != 1) { - fprintf(stderr, - "row[%s]: cs.s != ww.s (rule-10 break)\n", - r->label); - fail++; - } - } - - /* RUNTIME: cstage. */ - { - char wk[1024]; - snprintf(wk, sizeof wk, "%s/cs", dir); - mkdir(wk, 0755); - int got = build_run(cdrv, src, wk); - total++; - if (got != r->want) { - fprintf(stderr, - "row[%s][cstage]: exit=%d want=%d\n", - r->label, got, r->want); - fail++; - } - } - - /* RUNTIME: wwstage (the side #173 fixes). */ - if (have_ww) { - char wk[1024]; - snprintf(wk, sizeof wk, "%s/ww", dir); - mkdir(wk, 0755); - int got = build_run(wdrv, src, wk); - total++; - if (got != r->want) { - fprintf(stderr, - "row[%s][wwstage]: exit=%d want=%d\n", - r->label, got, r->want); - fail++; - } - } - -cleanup: - { - char rm[1100]; - snprintf(rm, sizeof rm, "rm -rf %s", dir); - runwait(rm); - } - } - - if (fail) { - fprintf(stderr, - "tryprop_tag_remap_run: %d/%d checks failed\n", fail, total); - return 1; - } - printf("tryprop_tag_remap_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/926_match_slice_variant_run.c b/test/wcc/926_match_slice_variant_run.c deleted file mode 100644 index 84b6926e..00000000 --- a/test/wcc/926_match_slice_variant_run.c +++ /dev/null @@ -1,258 +0,0 @@ -/* - * 926_match_slice_variant_run — Class B semantic test for task #19. - * Pre-fix wwstage emitted internally-consistent but wrong tag values - * for any `(scalar | []T)` widen/dispatch: every []T slot collapsed - * onto tag 0, so the match arm that fired was always the leading - * variant regardless of which arm the caller meant. cstage was - * correct on these shapes. - * - * 722 pins asm byte-id (the polarity sentinel). This file pins the - * end-to-end runtime: build through each driver and confirm both - * arms reachable, with payload survival on the slice arm. - * - * Matrix per rob (task #19): (u8 | []u8), reverse-order ([]u8 | u8), - * other scalar-vs-slice-of-same-primitive at i8/i32/u64/rune widths, - * and a three-arm (u8 | []u8 | str). - */ -#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; }; - -static const struct row rows[] = { - /* The canonical bytes.index shape: scalar arm returns 1, slice - * arm returns 2, and the slice arm also checks payload survives - * (sub.len + first byte) so a silent payload-drop isn't masked. */ - { "u8_slice_u8", - "fn pick(n: (u8 | []u8)) i32 = {\n" - " match (n) {\n" - " case let c: u8 => return c: i32;\n" - " case let s: []u8 => return 100i32 + (s.len: i32) + (s[0]: i32);\n" - " };\n" - " return -1;\n" - "};\n" - "export fn main() i32 = {\n" - " let buf: [3]u8;\n" - " buf[0] = 7u8; buf[1] = 8u8; buf[2] = 9u8;\n" - " if (pick(42u8) != 42) { return 11; };\n" - " if (pick(buf[0:3]) != 110) { return 12; };\n" - " return 0;\n" - "};\n", - 0 }, - /* Reverse-order: same shape with slice arm at idx 0. The - * shape-fallback in taggedvariantindex must yield idx 0 here, - * idx 1 for the prior row — the bug's asymmetry catch. */ - { "slice_u8_then_u8", - "fn pick(n: ([]u8 | u8)) i32 = {\n" - " match (n) {\n" - " case let s: []u8 => return 100i32 + (s.len: i32) + (s[0]: i32);\n" - " case let c: u8 => return c: i32;\n" - " };\n" - " return -1;\n" - "};\n" - "export fn main() i32 = {\n" - " let buf: [3]u8;\n" - " buf[0] = 7u8; buf[1] = 8u8; buf[2] = 9u8;\n" - " if (pick(buf[0:3]) != 110) { return 11; };\n" - " if (pick(42u8) != 42) { return 12; };\n" - " return 0;\n" - "};\n", - 0 }, - /* (i8 | []i8) — other signed-narrow primitive. */ - { "i8_slice_i8", - "fn pick(n: (i8 | []i8)) i32 = {\n" - " match (n) {\n" - " case let c: i8 => return c: i32;\n" - " case let s: []i8 => return 100i32 + (s.len: i32) + (s[0]: i32);\n" - " };\n" - " return -1;\n" - "};\n" - "export fn main() i32 = {\n" - " let buf: [2]i8;\n" - " buf[0] = 5i8; buf[1] = 6i8;\n" - " if (pick(11i8) != 11) { return 11; };\n" - " if (pick(buf[0:2]) != 107) { return 12; };\n" - " return 0;\n" - "};\n", - 0 }, - /* (i32 | []i32) — wider scalar payload arm. */ - { "i32_slice_i32", - "fn pick(n: (i32 | []i32)) i32 = {\n" - " match (n) {\n" - " case let c: i32 => return c;\n" - " case let s: []i32 => return 1000i32 + (s.len: i32) + s[0];\n" - " };\n" - " return -1;\n" - "};\n" - "export fn main() i32 = {\n" - " let buf: [2]i32;\n" - " buf[0] = 33i32; buf[1] = 44i32;\n" - " if (pick(77i32) != 77) { return 11; };\n" - " if (pick(buf[0:2]) != 1035) { return 12; };\n" - " return 0;\n" - "};\n", - 0 }, - /* (u64 | []u64) — 8-byte scalar arm; the scalar tag at idx 0 - * and the slice payload words must not collide. */ - { "u64_slice_u64", - "fn pick(n: (u64 | []u64)) i32 = {\n" - " match (n) {\n" - " case let c: u64 => return c: i32;\n" - " case let s: []u64 => return 1000i32 + (s.len: i32) + (s[0]: i32);\n" - " };\n" - " return -1;\n" - "};\n" - "export fn main() i32 = {\n" - " let buf: [2]u64;\n" - " buf[0] = 41u64; buf[1] = 42u64;\n" - " if (pick(99u64) != 99) { return 11; };\n" - " if (pick(buf[0:2]) != 1043) { return 12; };\n" - " return 0;\n" - "};\n", - 0 }, - /* (rune | []rune) — Hare's iterator-pair shape (a rune or a - * slice of runes). rune is u32-wide, []rune is 24B. */ - { "rune_slice_rune", - "fn pick(n: (rune | []rune)) i32 = {\n" - " match (n) {\n" - " case let r: rune => return r: i32;\n" - " case let s: []rune => return 1000i32 + (s.len: i32) + (s[0]: i32);\n" - " };\n" - " return -1;\n" - "};\n" - "export fn main() i32 = {\n" - " let buf: [2]rune;\n" - " buf[0] = 'a'; buf[1] = 'b';\n" - " if (pick('Z') != 90) { return 11; };\n" - " if (pick(buf[0:2]) != 1099) { return 12; };\n" - " return 0;\n" - "};\n", - 0 }, - /* Three-arm (u8 | []u8 | str): scalar at idx 0, slice at idx 1, - * str at idx 2. Verifies the slice axis composes with the - * pre-existing str shape pass — no regression on str arm. */ - { "u8_slice_u8_str", - "fn pick(n: (u8 | []u8 | str)) i32 = {\n" - " match (n) {\n" - " case let c: u8 => return c: i32;\n" - " case let s: []u8 => return 200i32 + (s.len: i32) + (s[0]: i32);\n" - " case let t: str => return 300i32 + (t.len: i32);\n" - " };\n" - " return -1;\n" - "};\n" - "export fn main() i32 = {\n" - " let buf: [3]u8;\n" - " buf[0] = 1u8; buf[1] = 2u8; buf[2] = 3u8;\n" - // Three-arm 32B-slot widening through chained if-call sites - // trips a separate sequential-push bug in both stages; bind - // each call to a let before checking to keep the row focused - // on variant-tag selection. - " let a: i32 = pick(5u8);\n" - " let b: i32 = pick(buf[0:3]);\n" - " let c: i32 = pick(\"hi\");\n" - " if (a != 5) { return 11; };\n" - " if (b != 204) { return 12; };\n" - " if (c != 302) { return 13; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char src[128], tmpdir[96], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/msv_run_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/msv_run_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/msv_run_%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", driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - runwait(rmcmd); - return -1; - } - - int got = runwait(outbin); - - runwait(rmcmd); - return got; -} - -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, - "match_slice_variant_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "match_slice_variant_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, - "match_slice_variant_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("match_slice_variant_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/927_is_nonident_run.c b/test/wcc/927_is_nonident_run.c deleted file mode 100644 index 9a370591..00000000 --- a/test/wcc/927_is_nonident_run.c +++ /dev/null @@ -1,288 +0,0 @@ -/* - * 927_is_nonident_run — `e is T` on a NON-IDENT scrutinee (#45). - * - * Pre-#45 wwstage cgtypetest resolved ONLY lhs.kind == N_IDENT; every - * other scrutinee shape (xs[i], p.field, call()) fell through with - * scrutoff = 0 + scrutt = nil, emitting `MOVQ (BP), AX; CMPQ $0, AX` - * — the tag read landed on the saved-BP word and the variant index - * clamped to 0. SILENT cs≠ww miscompile (cstage N_TYPETEST cgexprs - * the scrutinee and compares the real tag in AX); the regex fold-2a - * epilogue guard `insts[insts.len-1] is inst_match` is the consumer - * that surfaced it. The `as` twin already had the non-ident - * @asrt_spill fallback (#200); `is` mirrors cstage's no-spill shape - * instead (only the tag word is consumed). - * - * Rows pin every scrutinee shape: indexed (constant and len-1 index), - * dot-field, call-result, the `is []T` slice-variant axis on an - * indexed scrutinee, the nullable `(*T | void)` pointer-vs-null - * discriminator on both ident and non-ident scrutinees (review - * finding: the first non-ident arm tag-compared the pointer; the - * ident half was missing the nullable arm since before #45), and the - * ident control (asm hand-checked unchanged vs the pre-#45 compiler - * when the fix landed). Per row: - * w6c vs w6c_ww byte-id (rule 10 — cstage is the runtime-correct - * reference shape) + runtime via both the ww and ww_ww drivers. - */ -#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; -}; - -#define P48_TYPES \ - "type p48 = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64 };\n" \ - "type small = (p48 | bool);\n" - -static const struct row rows[] = { - /* The discovering repro: indexed scrutinee, constant and - * xs.len-1 index, asserted both polarities (a bool-built - * element must answer `is bool` true and `is p48` false — - * pre-#45 wwstage answered `is p48` TRUE, exit 2). */ - { "indexed", - P48_TYPES - "export fn main() i32 = {\n" - " let xs: []small;\n" - " let b: bool = true;\n" - " let v: small = b;\n" - " append(xs, v);\n" - " if (xs.len == 0 || !(xs[xs.len - 1] is bool)) {\n" - " return 1;\n" - " };\n" - " if (xs[xs.len - 1] is p48) {\n" - " return 2;\n" - " };\n" - " if (!(xs[0] is bool)) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, - /* Dot-field scrutinee. */ - { "dot_field", - P48_TYPES - "type holder = struct { v: small, k: i64 };\n" - "export fn main() i32 = {\n" - " let b: bool = true;\n" - " let h: holder = holder { v = b, k = 5 };\n" - " if (!(h.v is bool)) { return 1; };\n" - " if (h.v is p48) { return 2; };\n" - " return 0;\n" - "};\n", - 0 }, - /* Call-result scrutinee (register-class tagged return; the - * sret-class call stays #38b loud-stopped). */ - { "call_result", - "type val = (i64 | bool);\n" - "fn mk(flag: bool) val = {\n" - " if (flag) {\n" - " let b: bool = true;\n" - " return b;\n" - " };\n" - " let n: i64 = 7;\n" - " return n;\n" - "};\n" - "export fn main() i32 = {\n" - " if (!(mk(true) is bool)) { return 1; };\n" - " if (!(mk(false) is i64)) { return 2; };\n" - " if (mk(true) is i64) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, - /* `is []T` on an indexed scrutinee — the flatslicevariantidx - * axis of the non-ident variant resolution. */ - { "slice_variant_indexed", - "type val = (i64 | []u8);\n" - "export fn main() i32 = {\n" - " let xs: []val;\n" - " let buf: [2]u8 = [1u8, 2u8];\n" - " let s: []u8 = buf[0:2];\n" - " let v: val = s;\n" - " append(xs, v);\n" - " let n: i64 = 5;\n" - " let w: val = n;\n" - " append(xs, w);\n" - " if (!(xs[0] is []u8)) { return 1; };\n" - " if (!(xs[1] is i64)) { return 2; };\n" - " if (xs[0] is i64) { return 3; };\n" - " return 0;\n" - "};\n", - 0 }, - /* Nullable `(*T | void)` fold on a NON-IDENT scrutinee: the - * discriminator is pointer-vs-null, not tag-vs-index (cstage - * N_TYPETEST nullable arm emits CMPQ $0 + JE/JNE polarity). - * Surfaced in #45 review: the first non-ident arm compared the - * pointer against the variant index — `h.m is *t` on a non-null - * pointer answered FALSE (silent cs≠ww, wwstage exit 1). Both - * polarities, both states (ptr and void). */ - { "nullable_dot_field", - "type t = struct { a: i64 };\n" - "type maybe = (*t | void);\n" - "type holder = struct { m: maybe, k: i64 };\n" - "export fn main() i32 = {\n" - " let v: t = t { a = 5 };\n" - " let h: holder = holder { m = &v, k = 1 };\n" - " if (!(h.m is *t)) { return 1; };\n" - " if (h.m is void) { return 2; };\n" - " let h2: holder = holder { m = void, k = 2 };\n" - " if (h2.m is *t) { return 3; };\n" - " if (!(h2.m is void)) { return 4; };\n" - " return 0;\n" - "};\n", - 0 }, - /* Nullable IDENT scrutinee: the same missing nullable arm hit - * the ident path too (pre-existing at master, closed by the same - * shared compare choke-point). */ - { "nullable_ident", - "type t = struct { a: i64 };\n" - "type maybe = (*t | void);\n" - "export fn main() i32 = {\n" - " let v: t = t { a = 5 };\n" - " let m: maybe = &v;\n" - " if (!(m is *t)) { return 1; };\n" - " if (m is void) { return 2; };\n" - " let m2: maybe = void;\n" - " if (m2 is *t) { return 3; };\n" - " if (!(m2 is void)) { return 4; };\n" - " return 0;\n" - "};\n", - 0 }, - /* Ident control: the pre-existing slot-load path must be - * untouched (also hand-cmp'd vs the pre-#45 w6c_ww). */ - { "ident_control", - "type val = (i64 | bool);\n" - "export fn main() i32 = {\n" - " let b: bool = true;\n" - " let v: val = b;\n" - " if (!(v is bool)) { return 1; };\n" - " if (v is i64) { return 2; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static const char *g_bin; - -static int -compile_s(const char *tool, const char *src, const char *outpath) -{ - char cmd[1024]; - snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2>&1", - g_bin, tool, src, outpath); - return runwait(cmd); -} - -static int -file_eq(const char *a, const char *b) -{ - char cmd[1024]; - snprintf(cmd, sizeof cmd, "cmp -s %s %s", a, b); - return runwait(cmd) == 0; -} - -static int -run_driver(const char *driver, const char *src, const char *label) -{ - char tmpdir[128], outbin[160], rmcmd[200], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/isnonid_%d_d", getpid()); - mkdir(tmpdir, 0755); - snprintf(outbin, sizeof outbin, "%s/m", tmpdir); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1", - g_bin, driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - label, driver); - runwait(rmcmd); - return -1; - } - int got = runwait(outbin); - runwait(rmcmd); - return got; -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - static 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; - } - g_bin = bin; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int i = 0; i < n; i++) { - const struct row *r = &rows[i]; - char src[128], cs_s[128], ww_s[128]; - snprintf(src, sizeof src, "/tmp/isnonid_%d_%d.ww", - getpid(), i); - snprintf(cs_s, sizeof cs_s, "/tmp/isnonid_%d_%d_cs.s", - getpid(), i); - snprintf(ww_s, sizeof ww_s, "/tmp/isnonid_%d_%d_ww.s", - getpid(), i); - FILE *f = fopen(src, "wb"); - if (!f) return 1; - fputs("package main;\n\n", f); - fputs(r->src, f); - fclose(f); - - total++; - int cs_rc = compile_s("w6c", src, cs_s); - int ww_rc = compile_s("w6c_ww", src, ww_s); - if (cs_rc != 0 || ww_rc != 0) { - fprintf(stderr, "FAIL row[%s]: compile rc cs=%d " - "ww=%d\n", r->label, cs_rc, ww_rc); - fail++; - unlink(src); unlink(cs_s); unlink(ww_s); - continue; - } - if (!file_eq(cs_s, ww_s)) { - fprintf(stderr, "FAIL row[%s]: cs != ww .s\n", - r->label); - fail++; - } - int got_cs = run_driver("ww", src, r->label); - if (got_cs != r->want) { - fprintf(stderr, "FAIL row[%s] cstage: want %d " - "got %d\n", r->label, r->want, got_cs); - fail++; - } - char wwdrv[600]; - snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin); - if (access(wwdrv, X_OK) == 0) { - int got_ww = run_driver("ww_ww", src, r->label); - if (got_ww != r->want) { - fprintf(stderr, "FAIL row[%s] wwstage: " - "want %d got %d\n", - r->label, r->want, got_ww); - fail++; - } - } - unlink(src); unlink(cs_s); unlink(ww_s); - } - if (fail) { - fprintf(stderr, "is_nonident_run: %d/%d rows failed\n", - fail, total); - return 1; - } - printf("is_nonident_run: %d rows ok\n", total); - return 0; -} diff --git a/test/wcc/928_match_nonident_idx_run.c b/test/wcc/928_match_nonident_idx_run.c deleted file mode 100644 index dd68c782..00000000 --- a/test/wcc/928_match_nonident_idx_run.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * 928_match_nonident_idx_run — `match` on an N_INDEX scrutinee whose - * BASE is not an ident (#48). - * - * Pre-#48 wwstage matchscrutt's N_INDEX arm required ibase.kind == - * N_IDENT; an index over a struct field (`match (h.xs[i])` = N_INDEX - * over N_DOT) returned nil → cgmatch dispatched with scrutt = nil: - * every case arm's variant index clamped to 0 (CMPQ $0) and the - * @match_spill slot fell to the 16B default. SILENT cs≠ww miscompile - * (cstage N_MATCH reads the checker-stamped s->type for every - * scrutinee shape, cmd/w6c/cgen.c:7510); regex fold-2a's - * `match (re.insts[i])` is the consumer that surfaced it. The fix - * resolves the element type from the stamped .type_ (the #45/#67 - * stamped-carrier pattern), plus the load-half twin: cgindex's - * generic-fallback tagged-element load was the only arm missing the - * `slot > 24` R8 word (cgen.c:9106-9117), so a >24B-slot element via - * a non-ident base under-read the cursor. - * - * Rows pin: the discovering repro shape (field-base slice index, all - * three variants matched both ways), the regex shape (56B-slot - * inst-like union over r.insts[i]; payload reads stay within the 32B - * cursor — words past R8 are #43's deferred residual), a chained-dot - * base (o.in_.xs[i] — depth independence), and the ident - * scrutinee / ident-base array + slice index controls (byte-id at - * master per the #48 scoping probes; cs is untouched by the fix, so - * the per-row cs==ww cmp pins them unchanged). A call-BASE index - * (`mk()[0]`) is NOT covered: cgindex's element classification (esz + - * tagged) skips N_CALL bases — pre-existing sibling, filed separately. - * Per row: w6c vs w6c_ww byte-id (rule 10 — cstage is the - * runtime-correct reference) + runtime via both drivers. - */ -#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; -}; - -#define TU_TYPES \ - "type va = void;\n" \ - "type vb = bool;\n" \ - "type vc = size;\n" \ - "type tu = (va | vb | vc);\n" - -static const struct row rows[] = { - /* The discovering repro: slice field of a struct, indexed. - * Pre-#48 wwstage dispatched the vb element to the FIRST arm - * regardless of pattern (tag compared against 0) — exit 1. The - * third match pins arm-order independence (variant 1 matched - * from a non-first arm). */ - { "field_slice_idx", - TU_TYPES - "type holder = struct { xs: []tu, n: size };\n" - "export fn main() i32 = {\n" - " let sl: []tu = [(true: vb), ((7: size): vc)];\n" - " let h: holder;\n" - " h.xs = sl;\n" - " h.n = 2;\n" - " match (h.xs[0]) {\n" - " case let b: vb => { if (!(b: bool)) { return 2; }; };\n" - " case => return 1;\n" - " };\n" - " match (h.xs[1]) {\n" - " case let s: vc => { if ((s: size) != 7) { return 4; }; };\n" - " case => return 3;\n" - " };\n" - " match (h.xs[0]) {\n" - " case va => return 5;\n" - " case let b: vb => { if (!(b: bool)) { return 6; }; };\n" - " case vc => return 7;\n" - " };\n" - " return 0;\n" - "};\n", - 0 }, - /* The regex fold-2a shape: 56B-slot inst-like union (8B tag + - * 48B widest payload) matched over a struct-field slice index. - * The big variant drives slot/spill sizing past 24B (the R8 - * word both in cgindex's fallback load and cgmatch's spill); - * matched payloads read word 1 only — within the 32B cursor. */ - { "regex_inst_56b", - "type big = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64 };\n" - "type ilit = struct { r: i64 };\n" - "type imatch = void;\n" - "type inst = (big | ilit | imatch);\n" - "type re = struct { insts: []inst, n: i64 };\n" - "export fn main() i32 = {\n" - " let sl: []inst;\n" - " let l: ilit = ilit { r = 65 };\n" - " let v: inst = l;\n" - " append(sl, v);\n" - " let m: inst = void: imatch;\n" - " append(sl, m);\n" - " let r: re;\n" - " r.insts = sl;\n" - " r.n = 2;\n" - " match (r.insts[0]) {\n" - " case let x: ilit => { if (x.r != 65) { return 2; }; };\n" - " case => return 1;\n" - " };\n" - " match (r.insts[1]) {\n" - " case imatch => { };\n" - " case => return 3;\n" - " };\n" - " match (r.insts[0]) {\n" - " case big => return 4;\n" - " case let x: ilit => { if (x.r != 65) { return 5; }; };\n" - " case imatch => return 6;\n" - " };\n" - " return 0;\n" - "};\n", - 0 }, - /* Dot-DEPTH: the index base is a CHAINED dot (o.in_.xs[i] = - * N_INDEX over N_DOT over N_DOT) — pins that the stamped-carrier - * resolve is depth-independent (any non-ident base shape, not - * just one dot level). */ - { "dot_depth_idx", - TU_TYPES - "type inner = struct { xs: []tu, k: size };\n" - "type outer = struct { in_: inner, n: size };\n" - "export fn main() i32 = {\n" - " let sl: []tu = [(true: vb), ((9: size): vc)];\n" - " let o: outer;\n" - " o.in_.xs = sl;\n" - " o.in_.k = 1;\n" - " o.n = 2;\n" - " match (o.in_.xs[0]) {\n" - " case let b: vb => { if (!(b: bool)) { return 2; }; };\n" - " case => return 1;\n" - " };\n" - " match (o.in_.xs[1]) {\n" - " case va => return 3;\n" - " case let s: vc => { if ((s: size) != 9) { return 4; }; };\n" - " case vb => return 5;\n" - " };\n" - " return 0;\n" - "};\n", - 0 }, - /* Controls: ident scrutinee + ident-base array and slice index - * — the pre-existing matchscrutt/cgmatch paths the fix must not - * disturb (asm byte-id at master per the scoping probes). */ - { "ident_and_identbase_controls", - TU_TYPES - "export fn main() i32 = {\n" - " let v: tu = (true: vb);\n" - " match (v) {\n" - " case let b: vb => { if (!(b: bool)) { return 2; }; };\n" - " case => return 1;\n" - " };\n" - " let arr: [2]tu = [(true: vb), ((7: size): vc)];\n" - " match (arr[1]) {\n" - " case let s: vc => { if ((s: size) != 7) { return 4; }; };\n" - " case => return 3;\n" - " };\n" - " let sl: []tu = [(true: vb), ((7: size): vc)];\n" - " match (sl[0]) {\n" - " case let b: vb => { if (!(b: bool)) { return 6; }; };\n" - " case => return 5;\n" - " };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static const char *g_bin; - -static int -compile_s(const char *tool, const char *src, const char *outpath) -{ - char cmd[1024]; - snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2>&1", - g_bin, tool, src, outpath); - return runwait(cmd); -} - -static int -file_eq(const char *a, const char *b) -{ - char cmd[1024]; - snprintf(cmd, sizeof cmd, "cmp -s %s %s", a, b); - return runwait(cmd) == 0; -} - -static int -run_driver(const char *driver, const char *tmpdir, const char *src, - const char *label) -{ - char outbin[256], cmd[1024]; - snprintf(outbin, sizeof outbin, "%s/%s_bin", tmpdir, driver); - snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1", - g_bin, driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - label, driver); - return -1; - } - return runwait(outbin); -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - static 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; - } - g_bin = bin; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int i = 0; i < n; i++) { - const struct row *r = &rows[i]; - char tmpdir[128], src[160], cs_s[160], ww_s[160], rmcmd[192]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/matchnidx_%d_d_%d", - getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/matchnidx_%d_%d.ww", - tmpdir, getpid(), i); - snprintf(cs_s, sizeof cs_s, "%s/matchnidx_%d_%d_cs.s", - tmpdir, getpid(), i); - snprintf(ww_s, sizeof ww_s, "%s/matchnidx_%d_%d_ww.s", - tmpdir, getpid(), i); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return 1; } - fputs("package main;\n\n", f); - fputs(r->src, f); - fclose(f); - - total++; - int cs_rc = compile_s("w6c", src, cs_s); - int ww_rc = compile_s("w6c_ww", src, ww_s); - if (cs_rc != 0 || ww_rc != 0) { - fprintf(stderr, "FAIL row[%s]: compile rc cs=%d " - "ww=%d\n", r->label, cs_rc, ww_rc); - fail++; - runwait(rmcmd); - continue; - } - if (!file_eq(cs_s, ww_s)) { - fprintf(stderr, "FAIL row[%s]: cs != ww .s\n", - r->label); - fail++; - } - int got_cs = run_driver("ww", tmpdir, src, r->label); - if (got_cs != r->want) { - fprintf(stderr, "FAIL row[%s] cstage: want %d " - "got %d\n", r->label, r->want, got_cs); - fail++; - } - char wwdrv[600]; - snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin); - if (access(wwdrv, X_OK) == 0) { - int got_ww = run_driver("ww_ww", tmpdir, src, - r->label); - if (got_ww != r->want) { - fprintf(stderr, "FAIL row[%s] wwstage: " - "want %d got %d\n", - r->label, r->want, got_ww); - fail++; - } - } - runwait(rmcmd); - } - if (fail) { - fprintf(stderr, "match_nonident_idx_run: %d/%d rows failed\n", - fail, total); - return 1; - } - printf("match_nonident_idx_run: %d rows ok\n", total); - return 0; -} diff --git a/test/wcc/931_variant_typekey_run.c b/test/wcc/931_variant_typekey_run.c deleted file mode 100644 index d490d8ae..00000000 --- a/test/wcc/931_variant_typekey_run.c +++ /dev/null @@ -1,198 +0,0 @@ -/* - * 931_variant_typekey_run — Class B semantic sentinel for #66 - * (Phase-N step 3, the user-ruled type-keying flip). - * - * Locks in the behavioral capability the corpus does NOT exercise: - * distinguishing two co-variants that share a SHAPE (and, pre-flip, - * a name key) by NOMINAL TYPE identity. cstage's cg_variant_match - * (cmd/w6c/cgen.c:451) is what keeps `(str | linerr)` separable even - * though `type linerr = !str` unwraps to str; #66 brings wwstage's - * flatvariantidx to the same typeeq discipline (per-decl TY_NAMED ptr). - * - * Discriminating shape: a tagged union with the alias variant FIRST, - * widened from a value with no surface type name (a call return). - * Pre-#66 wwstage routed the value→tag direction through rhstargetname - * (surface name) + a str/scalar shape fallback that picked the FIRST - * shape-matching variant — i.e. the leading alias — so a plain `str` - * from a call was tagged as `linerr` (idx 0) instead of `str` (idx 1). - * A subsequent match then fired the wrong arm. cstage always type-keyed, - * so pre-#66 this was a cstage-vs-wwstage divergence; each row exits 10 - * on pre-#66 wwstage and 0 on cstage / post-#66 wwstage. The 990-997 - * byte-id gates do not cover this because the corpus has no - * same-shape co-variant whose name key and type key disagree. - * - * Pure runtime test: build through both drivers (cstage `ww`, wwstage - * `ww_ww`) and assert each arm fires for its matching input. - */ -#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; /* single-module program — written as p.ww */ - int want; -}; - -static const struct row rows[] = { - /* str axis. `(linerr | str)` with linerr = !str, alias first. - * classify(0) widens a str returned by a call (no surface name) - * — pre-#66 the shape fallback tags it as the leading str-shape - * variant linerr (idx 0) and the match returns 1 not 2. - * classify(1) widens a linerr (round-trip soundness). */ - { "linerr_str_alias_first_callret", - "package main;\n" - "type linerr = !str;\n" - "fn getstr() str = { return \"hi\"; };\n" - "fn produce(k: i32) (linerr | str) = {\n" - " if (k == 0) { return getstr(); };\n" - " let e: linerr = \"boom\";\n" - " return e;\n" - "};\n" - "export fn classify(k: i32) i32 = {\n" - " match (produce(k)) {\n" - " case let er: linerr => return 1;\n" - " case let v: str => return 2;\n" - " };\n" - "};\n" - "export fn main() i32 = {\n" - " if (classify(0) != 2) { return 10; };\n" - " if (classify(1) != 1) { return 11; };\n" - " return 0;\n" - "};\n", - 0 }, - /* scalar axis. `(ec | i32)` with ec = !i32, alias first. - * classify(0) widens an i32 returned by a call — pre-#66 the - * scalar shape fallback tags it as the leading scalar-shape - * variant ec (idx 0). Confirms the flip isn't str-specific. */ - { "errint_int_alias_first_callret", - "package main;\n" - "type ec = !i32;\n" - "fn getint() i32 = { return 7; };\n" - "fn produce(k: i32) (ec | i32) = {\n" - " if (k == 0) { return getint(); };\n" - " let e: ec = 9;\n" - " return e;\n" - "};\n" - "export fn classify(k: i32) i32 = {\n" - " match (produce(k)) {\n" - " case let er: ec => return 1;\n" - " case let v: i32 => return 2;\n" - " };\n" - "};\n" - "export fn main() i32 = {\n" - " if (classify(0) != 2) { return 10; };\n" - " if (classify(1) != 1) { return 11; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static int -write_file(const char *dir, const char *name, const char *body) -{ - char path[512]; - snprintf(path, sizeof path, "%s/%s", dir, name); - FILE *f = fopen(path, "wb"); - if (!f) return -1; - fputs(body, f); - fclose(f); - return 0; -} - -static int -run_driver(const char *driver, const struct row *r, int i) -{ - char tmpdir[96], cmd[2048]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/vtk_%d_%d", getpid(), i); - mkdir(tmpdir, 0755); - - if (write_file(tmpdir, "p.ww", r->src) != 0) return -1; - - snprintf(cmd, sizeof cmd, - "cd %s && %s build p.ww 2>/dev/null", tmpdir, driver); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - r->label, driver); - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - runwait(cmd); - return -1; - } - - char outbin[160]; - snprintf(outbin, sizeof outbin, "%s/p", tmpdir); - int got = runwait(outbin); - - snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir); - runwait(cmd); - return got; -} - -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, - "variant_typekey_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].path); - continue; - } - for (int i = 0; i < n; i++) { - int got = run_driver(drivers[d].path, &rows[i], i); - total++; - if (got != rows[i].want) { - fprintf(stderr, - "variant_typekey_run[%s][%s]: exit=%d want=%d\n", - drivers[d].name, rows[i].label, - got, rows[i].want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, - "variant_typekey_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("variant_typekey_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/938_tagged_structlit_payload_run.c b/test/wcc/938_tagged_structlit_payload_run.c deleted file mode 100644 index 4c382a3b..00000000 --- a/test/wcc/938_tagged_structlit_payload_run.c +++ /dev/null @@ -1,679 +0,0 @@ -/* - * 938_tagged_structlit_payload_run — #23 (fold-5b prereq): tagged-union - * CONSTRUCTION from an INLINE struct-literal payload whose fields are - * themselves tagged ((void|size) — the regex inst_repeat shape, - * ref/hare/regex/regex.ha:55-60). Pre-#23 the widen choke-point's - * struct-payload arm (cg_widen_tagged_store / cgwidentaggedstorebp) - * carried its OWN inline N_STRUCTLIT field loop — a parallel fill that - * drifted from cg_structlit_fill / cgstructlitfill: it had float/str/ - * slice/scalar arms but NO tagged-field widen arm, so a (void|T)-typed - * field's raw scalar landed in the field's TAG word (silent truncation - * past the first tagged field; both stages, byte-id — gate-blind; - * prober-9 PG5 evidence, re-confirmed at 6699158). The fix deletes the - * parallel loop and delegates to the canonical fill, which also - * inherits the nested-struct field arm. - * - * row | shape | want - * ---------------------+----------------------------------------+----- - * inst_repeat_shape | rep{id,origin,min=void,max=5:size} — | - * | the PG5b repro; 48B payload | 0 - * rep3_allsize | {id,min,max} all set via `: size` | - * | casts — PG5g; tag+payload word checks | 0 - * tagged_only16 | single (void|size) field — 16B payload | 0 - * prebound_tagged_field| min = mn (pre-bound (void|size) LOCAL | - * | — tagged→tagged subset inside the fill)| 0 - * str_and_tagged | {s:str, m:(void|size)} — 3-word str | - * | arm + tagged arm in one literal | 0 - * variant_pos0 | union (rep|lit) — struct variant tag 0 | 0 - * slice_elem_cursorfit | 24B payload through append + indexed | - * | match (union 32B = exactly the 4-word | - * | tagged cursor; >32B indexed reads are | - * | the open over-cap cursor residual, | - * | task #37 — NOT pinned here) | 0 - * nested_struct_field | nested struct-typed field literal in a | - * | union payload — delegation-inherited | - * | recursion arm (pre-#23: AX-only store) | 0 - * ellipsis_autofill | `...` autofill + tagged field — the | - * | fill's TK_ELLIPSIS zero-fill runs at | - * | the widened slot's payload base | 0 - * two_tagged_offset0 | tagged field at struct OFFSET 0, a 2nd | - * | tagged field, then a trailing scalar | 0 - * nested_and_tagged | nested struct field AND tagged field | - * | in ONE literal — fill→widen→fill, both | - * | recursion directions in one shape | 0 - * voidstr_field | (void|str) tagged field — 3-word str | - * | payload through the fill's widen arm; | - * | pre-#23 the garbage tag matched NO | - * | variant (fallthrough-proof readback) | 0 - * recursion_torture | 3-level tagged(struct{tagged(struct{ | - * | tagged(leaf-lit)})}) — the widen↔fill | - * | mutual recursion terminates + correct | 0 - * payload8_boundary | struct payload exactly 8B (single size | - * | field) — smallest-payload guard | 0 - * scalar_neighbor | scalar+f64+str payload, NO tagged | - * | field — emission no-regress guard | 0 - * - * Field readback goes through MATCH BINDINGS only: direct field reads - * on a struct-w/-tagged-field LOCAL are the open #24 (wwstage - * field(SB) fallback). Tagged-field inits use explicit `: size` - * casts in the #23 rows; the BARE untyped-int form (the old #33 - * wwstage mis-tag) is pinned by its own row (untyped_int_bare_widen) - * since the cgvariantmatch untyped arm landed. The chained-dot - * tagged-leaf read/assign family (#38a) is pinned by the - * chained_* rows below since the dot-spine tagged arms landed. - * - * 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 (936/941 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; -} - -/* noasm: skip the cs/ww byte-id check for rows whose READBACK needs an - * inner `match` on a tagged FIELD scrutinee — wwstage routes that load - * through a 4-word cursor scratch where cstage copies field words - * directly (PRE-EXISTING divergence, confirmed at master 6699158 on - * the same sources; construction asm is byte-id up to the readback — - * the regex-arc match-on-tagged-struct-field family, needs a filed - * task). The skip is printed loudly so it can't rot silently. */ -struct row { const char *label; const char *src; int want; int noasm; }; - -static const struct row rows[] = { - /* the PG5b repro verbatim: only id/origin survived pre-#23 - * (min's tag word read 0 = void by accident, max's read 5). */ - { "inst_repeat_shape", - "package main;\n" - "type inst_lit = rune;\n" - "type rep = struct {\n" - " id: size,\n" - " origin: size,\n" - " min: (void | size),\n" - " max: (void | size),\n" - "};\n" - "type inst = (inst_lit | rep);\n" - "export fn main() i32 = {\n" - " let v: inst = rep { id = 1: size, origin = 2: size, min = void, max = 5: size };\n" - " match (v) {\n" - " case let ir: rep => {\n" - " if (ir.id != 1) { return 1; };\n" - " if (ir.origin != 2) { return 2; };\n" - " if (!(ir.min is void)) { return 3; };\n" - " if (!(ir.max is size)) { return 4; };\n" - " if (ir.max as size != 5) { return 5; };\n" - " };\n" - " case let l: inst_lit => { return 6; };\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* PG5g: every tagged field set — pre-#23 the raw 2/5 landed in - * the tag words and both is-tests failed. */ - { "rep3_allsize", - "package main;\n" - "type inst_lit = rune;\n" - "type rep3 = struct { id: size, min: (void | size), max: (void | size) };\n" - "type inst = (inst_lit | rep3);\n" - "export fn main() i32 = {\n" - " let v: inst = rep3 { id = 1: size, min = 2: size, max = 5: size };\n" - " match (v) {\n" - " case let ir: rep3 => {\n" - " if (ir.id != 1) { return 1; };\n" - " if (!(ir.min is size)) { return 2; };\n" - " if (ir.min as size != 2) { return 3; };\n" - " if (!(ir.max is size)) { return 4; };\n" - " if (ir.max as size != 5) { return 5; };\n" - " };\n" - " case let l: inst_lit => { return 6; };\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* minimal payload: the tagged field IS the whole struct. */ - { "tagged_only16", - "package main;\n" - "type only = struct { m: (void | size) };\n" - "type u = (rune | only);\n" - "export fn main() i32 = {\n" - " let v: u = only { m = 7: size };\n" - " match (v) {\n" - " case let o: only => {\n" - " if (!(o.m is size)) { return 1; };\n" - " if (o.m as size != 7) { return 2; };\n" - " };\n" - " case => return 3;\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* field value is a pre-bound tagged LOCAL — the fill's widen arm - * takes the tagged→tagged identity-copy route. */ - { "prebound_tagged_field", - "package main;\n" - "type inst_lit = rune;\n" - "type rep3 = struct { id: size, min: (void | size), max: (void | size) };\n" - "type inst = (inst_lit | rep3);\n" - "export fn main() i32 = {\n" - " let mn: (void | size) = 2: size;\n" - " let v: inst = rep3 { id = 3: size, min = mn, max = void };\n" - " match (v) {\n" - " case let ir: rep3 => {\n" - " if (ir.id != 3) { return 1; };\n" - " if (!(ir.min is size)) { return 2; };\n" - " if (ir.min as size != 2) { return 3; };\n" - " if (!(ir.max is void)) { return 4; };\n" - " };\n" - " case let l: inst_lit => { return 5; };\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* str field (3-word arm) and tagged field in the same literal — - * the arms must not disturb each other's offsets. */ - { "str_and_tagged", - "package main;\n" - "type inst_lit = rune;\n" - "type named = struct { s: str, m: (void | size) };\n" - "type inst = (inst_lit | named);\n" - "export fn main() i32 = {\n" - " let v: inst = named { s = \"hi\", m = 9: size };\n" - " match (v) {\n" - " case let nm: named => {\n" - " if (len(nm.s) != 2) { return 1; };\n" - " if (!(nm.m is size)) { return 2; };\n" - " if (nm.m as size != 9) { return 3; };\n" - " };\n" - " case let l: inst_lit => { return 4; };\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* struct variant FIRST in the union — tag 0; pins that the fix - * is position-independent. */ - { "variant_pos0", - "package main;\n" - "type inst_lit = rune;\n" - "type rep3 = struct { id: size, min: (void | size), max: (void | size) };\n" - "type inst = (rep3 | inst_lit);\n" - "export fn main() i32 = {\n" - " let v: inst = rep3 { id = 1: size, min = void, max = 8: size };\n" - " match (v) {\n" - " case let ir: rep3 => {\n" - " if (ir.id != 1) { return 1; };\n" - " if (!(ir.min is void)) { return 2; };\n" - " if (!(ir.max is size)) { return 3; };\n" - " if (ir.max as size != 8) { return 4; };\n" - " };\n" - " case let l: inst_lit => { return 5; };\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* construction → append → indexed match, end-to-end. Payload 24B - * keeps the union at 32B — exactly the 4-word tagged cursor, so - * the indexed read is in-cap. The >32B indexed read truncates at - * the cursor (open over-cap residual, #22b/#36 family) — the - * 48B-payload rows above therefore match the LOCAL only. */ - { "slice_elem_cursorfit", - "package main;\n" - "type inst_lit = rune;\n" - "type rep1 = struct { id: size, m: (void | size) };\n" - "type inst = (inst_lit | rep1);\n" - "export fn main() i32 = {\n" - " let insts: []inst = [];\n" - " append(insts, ('a': inst_lit));\n" - " let v: inst = rep1 { id = 4: size, m = 6: size };\n" - " append(insts, v);\n" - " if (len(insts) != 2) { return 1; };\n" - " match (insts[1]) {\n" - " case let r: rep1 => {\n" - " if (r.id != 4) { return 2; };\n" - " if (!(r.m is size)) { return 3; };\n" - " if (r.m as size != 6) { return 4; };\n" - " };\n" - " case let l: inst_lit => { return 5; };\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* nested struct-typed field literal inside the union payload — - * rides the canonical fill's recursion arm the delegation - * inherits (pre-#23 the parallel loop stored AX only). */ - { "nested_struct_field", - "package main;\n" - "type inner = struct { a: size, b: size };\n" - "type outer = struct { id: size, in_: inner };\n" - "type u = (rune | outer);\n" - "export fn main() i32 = {\n" - " let v: u = outer { id = 1: size, in_ = inner { a = 2: size, b = 3: size } };\n" - " match (v) {\n" - " case let o: outer => {\n" - " if (o.id != 1) { return 1; };\n" - " if (o.in_.a != 2) { return 2; };\n" - " if (o.in_.b != 3) { return 3; };\n" - " };\n" - " case => return 4;\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* `...` autofill + tagged field (ken n7): the fill's TK_ELLIPSIS - * zero-fill loop must run at the widened slot's payload base, not - * the slot base — pre-#23 exit 3 (raw 5 in m's tag word). */ - { "ellipsis_autofill", - "package main;\n" - "type st = struct { a: u64, m: (void | size) };\n" - "export fn main() i32 = {\n" - " let e: (void | st) = st { m = 5: size, ... };\n" - " match (e) {\n" - " case let s: st => {\n" - " if (s.a != 0u64) { return 2; };\n" - " if (!(s.m is size)) { return 3; };\n" - " if (s.m as size != 5) { return 4; };\n" - " };\n" - " case void => { return 9; };\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* tagged field at struct OFFSET 0 plus a second tagged field and - * a TRAILING scalar (ken n2b) — pins offset-0 tag placement and - * the scalar offset computed past two 16B tagged fields. */ - { "two_tagged_offset0", - "package main;\n" - "type d2 = struct { m: (void | size), n: (void | i64), z: u64 };\n" - "export fn main() i32 = {\n" - " let e: (void | d2) = d2 { m = 4: size, n = (-9): i64, z = 77u64 };\n" - " match (e) {\n" - " case let s: d2 => {\n" - " if (!(s.m is size)) { return 2; };\n" - " if (s.m as size != 4) { return 3; };\n" - " if (!(s.n is i64)) { return 4; };\n" - " if (s.n as i64 != -9) { return 5; };\n" - " if (s.z != 77u64) { return 6; };\n" - " };\n" - " case void => { return 9; };\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* nested struct field AND tagged field in the SAME literal (ken - * n5b) — exercises fill→fill (nested) and fill→widen (tagged) - * from one delegated call. */ - { "nested_and_tagged", - "package main;\n" - "type inn = struct { v: u64 };\n" - "type outt = struct { i: inn, m: (void | size) };\n" - "export fn main() i32 = {\n" - " let e: (void | outt) = outt { i = inn { v = 11u64 }, m = 8: size };\n" - " match (e) {\n" - " case let s: outt => {\n" - " if (s.i.v != 11u64) { return 2; };\n" - " if (!(s.m is size)) { return 3; };\n" - " if (s.m as size != 8) { return 4; };\n" - " };\n" - " case void => { return 9; };\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* (void|str) tagged field — 3-word str payload through the fill's - * widen arm. Readback initialises `got = 9` BEFORE the inner - * match so a garbage tag matching NO variant can't fall through - * to a fake 0 (pre-#23 the raw str ptr landed in the tag word - * and exactly that fallthrough faked a pass). */ - { "voidstr_field", - "package main;\n" - "type st = struct { id: size, s: (void | str) };\n" - "type u = (rune | st);\n" - "export fn main() i32 = {\n" - " let v: u = st { id = 3: size, s = \"hello\" };\n" - " match (v) {\n" - " case let o: st => {\n" - " if (o.id != 3) { return 1; };\n" - " let got: i32 = 9;\n" - " match (o.s) {\n" - " case let sv: str => {\n" - " if (len(sv) == 5) { got = 0; } else { got = 2; };\n" - " };\n" - " case void => { got = 3; };\n" - " };\n" - " return got;\n" - " };\n" - " case => return 4;\n" - " };\n" - " return 5;\n" - "};\n", 0, 1 }, - /* 3-level widen↔fill mutual recursion: tagged(struct{tagged( - * struct{tagged(struct-lit)})}) — termination AND correctness of - * the #23 delegation cycle (pre-#23 exit 4: mid's tag dropped). */ - { "recursion_torture", - "package main;\n" - "type leaf = struct { p: size };\n" - "type t1 = (void | leaf);\n" - "type mid = struct { t: t1 };\n" - "type t2 = (void | mid);\n" - "type outr = struct { id: size, t: t2 };\n" - "type u = (rune | outr);\n" - "export fn main() i32 = {\n" - " let v: u = outr { id = 1: size, t = mid { t = leaf { p = 9: size } } };\n" - " match (v) {\n" - " case let o: outr => {\n" - " if (o.id != 1) { return 1; };\n" - " match (o.t) {\n" - " case let m: mid => {\n" - " match (m.t) {\n" - " case let l: leaf => {\n" - " if (l.p != 9) { return 2; };\n" - " return 0;\n" - " };\n" - " case void => { return 3; };\n" - " };\n" - " };\n" - " case void => { return 4; };\n" - " };\n" - " };\n" - " case => return 5;\n" - " };\n" - " return 6;\n" - "};\n", 0, 1 }, - /* struct payload exactly 8B — the smallest payload through the - * widen→fill delegation (boundary guard; worked pre-#23 too). */ - { "payload8_boundary", - "package main;\n" - "type one = struct { a: size };\n" - "type u = (rune | one);\n" - "export fn main() i32 = {\n" - " let v: u = one { a = 42: size };\n" - " match (v) {\n" - " case let o: one => { if (o.a != 42) { return 1; }; };\n" - " case => return 2;\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* no tagged field at all — the previously-working scalar/float/ - * str arms must emit unchanged through the delegation. */ - { "scalar_neighbor", - "package main;\n" - "type plain = struct { a: size, f: f64, s: str };\n" - "type u = (rune | plain);\n" - "export fn main() i32 = {\n" - " let v: u = plain { a = 11: size, f = 2.5, s = \"xyz\" };\n" - " match (v) {\n" - " case let p: plain => {\n" - " if (p.a != 11) { return 1; };\n" - " if (p.f != 2.5) { return 2; };\n" - " if (len(p.s) != 3) { return 3; };\n" - " };\n" - " case => return 4;\n" - " };\n" - " return 0;\n" - "};\n", 0, 0 }, - /* #33: a BARE untyped-int init into (void|T) — pre-fix wwstage's - * variant scan fell to the str/slice shape fallback whose first - * non-str/slice variant is `void` (tag 0 stored, is-test expects - * 1; cstage resolved via type_assignable). Pins let-init, assign - * after void, arg widen, the bool-leading skip, and the signed - * (void|i64) variant (ken hB3); the cast form rides alongside as - * the no-drift control (it was always right). */ - /* #38a: chained-dot TAGGED leaf — the dot-spine walkers (value - * chain + ptr chain, both stages) had no TY_TAGGED leaf arm, so - * READS pulled one word (tag; as/match consumers read stale DX - * payload, ken x5c want-28-got-20) and ASSIGNS stored the rhs - * over the TAG slot (ken x5d). Six rows: literal-init read, - * assign-then-read, depth-3 chain read-before-write (ken hB1), - * (void|str) 32B box incl. void-over-str write (ken hB2), - * *outer root, mid-chain *rep. The INDEX-spine sibling - * (xs[i].min) is filed separately (task #58) — NOT a row here. */ - { "chained_tagged_literal_read", - "package main;\n" - "type rep = struct { id: size, min: (void | size), name: (void | str) };\n" - "type outer = struct { tag: size, r: rep };\n" - "export fn main() i32 = {\n" - " let o = outer { tag = 4: size, r = rep { id = 6: size, min = 8: size, name = void } };\n" - " if (o.tag != 4) { return 1; };\n" - " if (o.r.id != 6) { return 2; };\n" - " if (!(o.r.min is size)) { return 3; };\n" - " if (o.r.min as size != 8) { return 4; };\n" - " if (!(o.r.name is void)) { return 5; };\n" - " return 0;\n" - "};\n", 0, 0 }, - { "chained_tagged_assign", - "package main;\n" - "type rep = struct { id: size, min: (void | size) };\n" - "type outer = struct { tag: size, r: rep };\n" - "export fn main() i32 = {\n" - " let o: outer = outer { tag = 4: size, r = rep { id = 6: size, min = void } };\n" - " o.r.min = 8: size;\n" - " if (!(o.r.min is size)) { return 1; };\n" - " if (o.r.min as size != 8) { return 2; };\n" - " o.r.min = 9;\n" - " if (!(o.r.min is size)) { return 3; };\n" - " if (o.r.min as size != 9) { return 4; };\n" - " return 0;\n" - "};\n", 0, 0 }, - { "chained_tagged_depth2", - "package main;\n" - "type rep = struct { id: size, min: (void | size) };\n" - "type mid = struct { m: size, r: rep };\n" - "type outer = struct { tag: size, w: mid };\n" - "export fn main() i32 = {\n" - " let o = outer { tag = 4: size, w = mid { m = 2: size, r = rep { id = 6: size, min = 5: size } } };\n" - " if (!(o.w.r.min is size)) { return 5; };\n" - " if (o.w.r.min as size != 5) { return 6; };\n" - " o.w.r.min = 8: size;\n" - " if (o.w.m != 2) { return 1; };\n" - " if (o.w.r.id != 6) { return 2; };\n" - " if (!(o.w.r.min is size)) { return 3; };\n" - " if (o.w.r.min as size != 8) { return 4; };\n" - " return 0;\n" - "};\n", 0, 0 }, - { "chained_tagged_strvariant", - "package main;\n" - "type rep = struct { id: size, nm: (void | str) };\n" - "type outer = struct { tag: size, r: rep };\n" - "export fn main() i32 = {\n" - " let o = outer { tag = 4: size, r = rep { id = 6: size, nm = void } };\n" - " o.r.nm = \"hello\";\n" - " if (!(o.r.nm is str)) { return 1; };\n" - " if ((o.r.nm as str).len != 5) { return 2; };\n" - " match (o.r.nm) {\n" - " case let s: str => { if (s.len != 5) { return 3; }; };\n" - " case void => { return 4; };\n" - " };\n" - " o.r.nm = void;\n" - " if (!(o.r.nm is void)) { return 5; };\n" - " if (o.r.id != 6) { return 6; };\n" - " return 0;\n" - "};\n", 0, 0 }, - { "chained_tagged_ptrroot", - "package main;\n" - "type rep = struct { id: size, min: (void | size) };\n" - "type outer = struct { tag: size, r: rep };\n" - "export fn main() i32 = {\n" - " let o = outer { tag = 4: size, r = rep { id = 6: size, min = void } };\n" - " let p: *outer = &o;\n" - " p.r.min = 8: size;\n" - " if (p.r.id != 6) { return 1; };\n" - " if (!(p.r.min is size)) { return 2; };\n" - " if (p.r.min as size != 8) { return 3; };\n" - " return 0;\n" - "};\n", 0, 0 }, - { "chained_tagged_ptrmid", - "package main;\n" - "type rep = struct { id: size, min: (void | size) };\n" - "type outer = struct { tag: size, p: *rep };\n" - "export fn main() i32 = {\n" - " let r = rep { id = 6: size, min = void };\n" - " let o = outer { tag = 4: size, p = &r };\n" - " o.p.min = 8: size;\n" - " if (o.p.id != 6) { return 1; };\n" - " if (!(o.p.min is size)) { return 2; };\n" - " if (o.p.min as size != 8) { return 3; };\n" - " return 0;\n" - "};\n", 0, 0 }, - { "untyped_int_bare_widen", - "package main;\n" - "fn take(v: (void | size)) i32 = {\n" - " if (v is size) { return (v as size): i32; };\n" - " return -1;\n" - "};\n" - "export fn main() i32 = {\n" - " let e: (void | size) = 5;\n" - " if (!(e is size)) { return 1; };\n" - " if (e as size != 5) { return 2; };\n" - " let mn: (void | size) = void;\n" - " mn = 7;\n" - " if (!(mn is size)) { return 3; };\n" - " if (mn as size != 7) { return 4; };\n" - " let b: (bool | u64) = 9;\n" - " if (!(b is u64)) { return 5; };\n" - " if (take(9) != 9) { return 6; };\n" - " let cf: (void | size) = 5: size;\n" - " if (!(cf is size)) { return 7; };\n" - " if (cf as size != 5) { return 8; };\n" - " let f: (void | i64) = 9;\n" - " if (!(f is i64)) { return 9; };\n" - " if (f as i64 != 9) { return 10; };\n" - " return 0;\n" - "};\n", 0, 0 }, -}; - -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/tsp_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tsp_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tsp_%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 -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/tsp_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/tsp_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/tsp_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].noasm) { - printf("row[%s]: asm byte-id SKIPPED " - "(pre-existing match-on-tagged-field " - "cs/ww readback divergence, " - "master-confirmed)\n", rows[i].label); - continue; - } - total++; - if (asm_byte_identical(bin, &rows[i], i) != 0) fail++; - } - } - - if (fail) { - fprintf(stderr, "tagged_structlit_payload: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("tagged_structlit_payload: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/944_idx_tagged_field_run.c b/test/wcc/944_idx_tagged_field_run.c deleted file mode 100644 index 4ede87a8..00000000 --- a/test/wcc/944_idx_tagged_field_run.c +++ /dev/null @@ -1,369 +0,0 @@ -/* - * 944_idx_tagged_field_run — #58 INDEXED-element TAGGED-field read+assign. - * - * `xs[i].f` where f is a TAGGED union field of a struct array element was - * broken on BOTH stages, byte-IDENTICAL and SILENT (#263 gate-blind; the - * byte-id gate cannot see it, runtime is the only net). The `arr[i].field` - * spine had arms for array / str/slice / float but NO TY_TAGGED arm, so a - * tagged field fell to the single-word scalar fallback: - * READ: loaded only the tag word; the payload cursor (DX/CX/R8) was - * never loaded -> `xs[i].f as T` read stale DX (#38a residual). - * ASSIGN: DOUBLY broken -- stored the raw unboxed scalar into the TAG - * slot, corrupting the box (never boxed, never wrote payload). - * The fix inserts a TY_TAGGED arm before each scalar fallback, both - * stages (align-BOTH, one commit). READ: AX=&xs[i] -> cursor (AX=tag, - * DX/CX/R8=payload), tag LAST. ASSIGN: box the concrete scalar variant - * (tag-index + payload), store over the indexed dest spine. - * - * row | shape | want - * -------------+------------------------------------------------+----- - * const_read | xs[0].m as size ((void|size), tag1) | 0 - * var_read | xs[i].m as size variable index | 0 - * tag2_read | xs[k].m as size ((void|bool|size), TAG2) | 0 - * assign_back | xs[0].m = 8:size; readback via *row ptr | 0 - * - * Pre-fix (base cc896bd): const_read/var_read/tag2_read read stale DX - * (payload dropped); assign_back corrupts the box (scalar-into-tag) so - * the ptr-readback control sees the un-overwritten constructed payload - * (99, not 8) -- all four FAIL both stages, cs==ww byte-id (the teeth). - * MULTIPLE tag positions (tag1-of-2 + tag2-of-3) are mandatory: an - * aligned-tag-only pin false-greens the assign scalar-into-tag-slot - * corruption. Post-fix: all four 0/0, cs/ww still byte-identical. - * - * The >32B / multi-word / float-payload arms LOUD-STOP (their real emission - * is not yet wired — #114). These shapes are NOT unconstructible: a wide-box - * union is built via a NARROW variant, so each tripwire is REACHABLE and is - * pinned expect-loud by the cfrows[] rows below. #114 wires the real emission - * and graduates those rows to runtime-value pins. - */ -#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; -} - -struct row { const char *label; const char *src; int want; }; - -static const struct row rows[] = { - { "const_read", - "package main;\n" - "type opt = (void | size);\n" - "type row = struct { id: int, m: opt };\n" - "export fn main() i32 = {\n" - " let xs: [2]row = [\n" - " row { id = 1, m = 7: size },\n" - " row { id = 2, m = 8: size },\n" - " ];\n" - " let v: size = xs[0].m as size;\n" - " if (v != 7) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, - { "var_read", - "package main;\n" - "type opt = (void | size);\n" - "type row = struct { id: int, m: opt };\n" - "export fn main() i32 = {\n" - " let xs: [2]row = [\n" - " row { id = 1, m = 7: size },\n" - " row { id = 2, m = 8: size },\n" - " ];\n" - " let i: size = 1;\n" - " let v: size = xs[i].m as size;\n" - " if (v != 8) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, - { "tag2_read", - "package main;\n" - "type opt3 = (void | bool | size);\n" - "type row = struct { id: int, m: opt3 };\n" - "export fn main() i32 = {\n" - " let xs: [2]row = [\n" - " row { id = 1, m = 5: size },\n" - " row { id = 2, m = 9: size },\n" - " ];\n" - " let i: size = 1;\n" - " let v: size = xs[i].m as size;\n" - " if (v != 9) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, - { "assign_back", - "package main;\n" - "type opt = (void | size);\n" - "type row = struct { id: int, m: opt };\n" - "export fn main() i32 = {\n" - " let xs: [2]row = [\n" - " row { id = 1, m = 99: size },\n" - " row { id = 2, m = 2: size },\n" - " ];\n" - " xs[0].m = 8: size;\n" - " let p: *row = &xs[0];\n" - " let v: size = p.m as size;\n" - " if (v != 8) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, -}; - -/* - * #58 LOUD-STOP TRIPWIRES — expect-compile-fail rows. - * - * The >32B / multi-word / float-payload arms of the indexed tagged-field - * read+store are NOT silently miscompiled: they LOUD-STOP (rule 7). Earlier - * triage believed these shapes were "unconstructible at HEAD" — they are NOT. - * A wide-box union (largest variant >32B) is constructible via a NARROW/scalar - * variant: `(void | size | [5]size)` initialised `m = 7: size` builds and runs - * fine; only reading/assigning that field via the indexed spine reaches the - * tripwire. So each tripwire is REACHABLE and must be pinned as expect-loud: - * a build that SUCCEEDS here would mean the loud-stop regressed into a silent - * miscompile. Each row must (a) fail the build and (b) emit a "#58" tripwire on - * stderr, attributing the CFAIL to this fold (not the #54/#23 construction - * hole, which only fires on STRUCT-LITERAL payloads). When #114 wires real - * >32B box+memcpy emission, it replaces the tripwires AND graduates these rows - * to runtime-value pins. - */ -struct cfrow { const char *label; const char *src; }; - -static const struct cfrow cfrows[] = { - { "big_read", /* >32B box read tripwire */ - "package main;\n" - "type big = (void | size | [5]size);\n" - "type row = struct { id: int, m: big };\n" - "export fn main() i32 = {\n" - " let xs: [2]row = [\n" - " row { id = 1, m = 7: size },\n" - " row { id = 2, m = 8: size },\n" - " ];\n" - " let v: size = xs[0].m as size;\n" - " if (v != 7) { return 1; };\n" - " return 0;\n" - "};\n" }, - { "big_assign", /* >32B box store tripwire */ - "package main;\n" - "type big = (void | size | [5]size);\n" - "type row = struct { id: int, m: big };\n" - "export fn main() i32 = {\n" - " let xs: [2]row = [\n" - " row { id = 1, m = 7: size },\n" - " row { id = 2, m = 8: size },\n" - " ];\n" - " xs[0].m = 9: size;\n" - " return 0;\n" - "};\n" }, - { "midword_assign", /* multi-word (>16B, <=32B box) store tripwire */ - "package main;\n" - "type mid = (void | size | [3]size);\n" - "type row = struct { id: int, m: mid };\n" - "export fn main() i32 = {\n" - " let xs: [2]row = [\n" - " row { id = 1, m = 7: size },\n" - " row { id = 2, m = 8: size },\n" - " ];\n" - " xs[0].m = 9: size;\n" - " return 0;\n" - "};\n" }, - { "float_assign", /* float-payload store tripwire */ - "package main;\n" - "type fo = (void | f64);\n" - "type row = struct { id: int, m: fo };\n" - "export fn main() i32 = {\n" - " let xs: [2]row = [\n" - " row { id = 1, m = 1.0: f64 },\n" - " row { id = 2, m = 2.0: f64 },\n" - " ];\n" - " xs[0].m = 3.0: f64;\n" - " return 0;\n" - "};\n" }, -}; - -static int -file_has(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 -run_cfail(const char *driver, const struct cfrow *r, int i) -{ - char tmpdir[96], src[160], outbin[160], errf[160], rmcmd[176], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/itf_cf_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/itf_cf_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/itf_cf_%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, - "timeout 20 %s build -o %s %s >/dev/null 2>%s", - driver, outbin, src, errf); - int brc = runwait(cmd); - int ok = (brc != 0) && file_has(errf, "#58"); - if (!ok) - fprintf(stderr, - "row[%s]: %s build rc=%d, want CFAIL with \"#58\" tripwire\n", - r->label, driver, brc); - runwait(rmcmd); - return ok ? 0 : 1; -} - -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/itf_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/itf_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/itf_%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, - "timeout 20 %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; -} - -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/itf_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/itf_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/itf_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 cn = (int)(sizeof cfrows / sizeof cfrows[0]); - int total = 0, fail = 0; - - for (int i = 0; i < n; i++) { - total++; - if (run_driver(cdrv, &rows[i], i) != 0) fail++; - } - /* cstage loud-stop tripwires (cgen.c) — expect CFAIL with "#58". */ - for (int i = 0; i < cn; i++) { - total++; - if (run_cfail(cdrv, &cfrows[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++) { - total++; - if (asm_byte_identical(bin, &rows[i], i) != 0) fail++; - } - /* wwstage loud-stop tripwires (cgenexpr.ww). */ - for (int i = 0; i < cn; i++) { - total++; - if (run_cfail(wdrv, &cfrows[i], cn + i) != 0) fail++; - } - } - - if (fail) { - fprintf(stderr, "idx_tagged_field: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("idx_tagged_field: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/944_tagged_widen_arg_run.c b/test/wcc/944_tagged_widen_arg_run.c deleted file mode 100644 index ef106163..00000000 --- a/test/wcc/944_tagged_widen_arg_run.c +++ /dev/null @@ -1,282 +0,0 @@ -/* - * 944_tagged_widen_arg_run — #55 tagged-SOURCE arg widened into a wider - * tagged-union PARAM slot (slot-DIFFER). - * - * A call arg whose own type is a NARROWER tagged union (un16=16B) passed - * where the param is a WIDER tagged union (un3=24B). wwstage pushargsrev - * mis-pushed every leg but the lucky aligned-ident: the slot-DIFFER arg - * fell past the slot-gated aistagged arms to the concrete-variant scalar - * boxing (taggedvariantindex<0 → tag clamped to 0 → word0 boxed, real tag - * + high words dropped, NO variant remap). cstage routes EVERY tagged - * source through cg_widen_tagged_store (cgen.c:2982 src_is_tagged) → - * scratch + tag-remap, correct on all 8. - * - * The fix (ww align-UP, 3 coordinated arms in cgenutil.ww): - * 1. the IDENT aistagged gate is now slot-gated (was ungated TRUE) so a - * slot-DIFFER ident falls to the widen scratch instead of the no- - * remap natural push (the prefix-luck); - * 2. pushargsrev's widensz>0 arm routes a tagged source (argistagged) - * through @tagscr + cgwidentaggedstore + push high→low (the - * pname/argistuple twin); - * 3. cgwidentaggedstore's cursor arm (INDEX/DOT ≤32B) spills by the - * SOURCE width, zero-pads, and tag-REMAPS (mirrors cstage's - * cg_widen_tagged_store cursor + shared pad/remap tail, cgen.c:2695- - * 2714) — pre-#55 it spilled by the dst slot (stale high regs) and - * skipped the remap. - * - * row | leg | tag | want - * ----------------+-------+-----------+----- - * ident_aligned | e | i64=tag0 | 42 - * deref_aligned | *p | i64=tag0 | 42 - * index_aligned | xs[0] | i64=tag0 | 42 - * dot_aligned | w.f | i64=tag0 | 42 - * ident_remap | e | i64=tag1 | 42 (un16b reorders i64→tag1) - * deref_remap | *p | i64=tag1 | 42 - * index_remap | xs[0] | i64=tag1 | 42 - * dot_remap | w.f | i64=tag1 | 42 - * - * The remap rows carry the MISALIGNED tag: un16b=(bool|i64) puts i64 at - * tag1, widened into un3b=(i64|bool|s16) where i64 is tag0 — so the source - * tag MUST be rewritten or the callee reads the wrong arm. An aligned-only - * suite false-greens a remap-less fix (the exact trap here, ken #55). - * - * Pre-fix: deref/index/dot legs cs42/ww!=42 (silent wrong); aligned-ident - * ww42 by LUCK but asm cs!=ww. Post-fix: all 8 cs42/ww42, all 8 byte-id. - */ -#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; }; - -#define TAKE_ALIGNED \ - "type s16 = struct { a: i64, b: i64 };\n" \ - "type un16 = (i64 | bool);\n" \ - "type un3 = (i64 | bool | s16);\n" \ - "fn take(e: un3) i64 = {\n" \ - " match (e) {\n" \ - " case let v: i64 => { return v; };\n" \ - " case let b: bool => { return -1; };\n" \ - " case let s: s16 => { return s.a + s.b; };\n" \ - " };\n" \ - "};\n" - -#define TAKE_REMAP \ - "type s16 = struct { a: i64, b: i64 };\n" \ - "type un16b = (bool | i64);\n" \ - "type un3b = (i64 | bool | s16);\n" \ - "fn take(e: un3b) i64 = {\n" \ - " match (e) {\n" \ - " case let v: i64 => { return v; };\n" \ - " case let b: bool => { return -1; };\n" \ - " case let s: s16 => { return s.a + s.b; };\n" \ - " };\n" \ - "};\n" - -static const struct row rows[] = { - { "ident_aligned", - "package main;\n" TAKE_ALIGNED - "export fn main() i32 = {\n" - " let v: un16 = (42: i64);\n" - " if (take(v) != 42) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, - { "deref_aligned", - "package main;\n" TAKE_ALIGNED - "export fn main() i32 = {\n" - " let v: un16 = (42: i64);\n" - " let p: *un16 = &v;\n" - " if (take(*p) != 42) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, - { "index_aligned", - "package main;\n" TAKE_ALIGNED - "export fn main() i32 = {\n" - " let xs: [2]un16 = [(42: i64), (7: i64)];\n" - " if (take(xs[0]) != 42) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, - { "dot_aligned", - "package main;\n" TAKE_ALIGNED - "type wrap = struct { f: un16 };\n" - "export fn main() i32 = {\n" - " let w: wrap = wrap { f = (42: i64) };\n" - " if (take(w.f) != 42) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, - /* misaligned-tag rows — i64 is tag1 in un16b, tag0 in un3b: the - * remap MUST fire or the callee reads the wrong match arm. */ - { "ident_remap", - "package main;\n" TAKE_REMAP - "export fn main() i32 = {\n" - " let v: un16b = (42: i64);\n" - " if (take(v) != 42) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, - { "deref_remap", - "package main;\n" TAKE_REMAP - "export fn main() i32 = {\n" - " let v: un16b = (42: i64);\n" - " let p: *un16b = &v;\n" - " if (take(*p) != 42) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, - { "index_remap", - "package main;\n" TAKE_REMAP - "export fn main() i32 = {\n" - " let xs: [2]un16b = [(42: i64), (7: i64)];\n" - " if (take(xs[0]) != 42) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, - { "dot_remap", - "package main;\n" TAKE_REMAP - "type wrap = struct { f: un16b };\n" - "export fn main() i32 = {\n" - " let w: wrap = wrap { f = (42: i64) };\n" - " if (take(w.f) != 42) { return 1; };\n" - " return 0;\n" - "};\n", 0 }, -}; - -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/twa_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/twa_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/twa_%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, - "timeout 20 %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; -} - -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/twa_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/twa_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/twa_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++) { - total++; - if (asm_byte_identical(bin, &rows[i], i) != 0) fail++; - } - } - - if (fail) { - fprintf(stderr, "tagged_widen_arg: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("tagged_widen_arg: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/944_variant_chain_b95_run.c b/test/wcc/944_variant_chain_b95_run.c index 266580dc..b9189147 100644 --- a/test/wcc/944_variant_chain_b95_run.c +++ b/test/wcc/944_variant_chain_b95_run.c @@ -1,74 +1,23 @@ /* - * 944_variant_chain_b95_run — #95: variant-match chain-membership arm - * (pass 1b) + structural fallback (c2), BOTH stages fused (cstage - * cg_tag_for_variant + wwstage flatvariantidxt are the same route). - * Pre-#95 a NAMED struct source that was not pointer-identical to a - * NAMED variant fell through every pass and the widen stored tag 0 — - * both stages, byte-identical, gate-blind (the worst class on the - * board: silent wrong tag on VALID code). + * 944_variant_chain_b95_run -- SLIMMED to the one irreducible asymmetric row. * - * Row sources are ken's #95 first-position oracle - * (.ai/ken-95-oracle.md, probes /tmp/b95/src + kb4/kb5) and rob's - * fold spec (.ai/rob-95-spec.md). Reference: harec - * tagged_select_subtype P1/P2/P3 (ref/harec/src/types.c:702-739, - * :988-1140). + * The #95 variant-match chain-membership corpus (22 rows) migrated to fold-2 + * homes (#5-C5): the K_RUN value rows -> test/lang/variant_chain_b95_test.ww + * (@test + T2 byte-id); the two #81-byte-id-waived CALL-source rows -> + * test/lang/variant_chain_b95_runonly_test.ww (value-only); the K_BUILDERR + * reject rows -> test/wcc/data/vchain_/case.ww runww //ww:error carriers. * - * row | shape (oracle row) | cs/ww - * --------------------+-------------------------------------+------ - * chain_1lvl_i | kb5_v2s1i HEADLINE: depth-1 ident | - * | src base → (void|al0) — was both- | - * | wrong-IDENTICAL byte-id tag 0 | 0/0 - * chain_2lvl_i | kb95_2lvl_i: ident src depth-2 — | - * | the class extends to any depth | 0/0 - * chain_deep_src | kb95_deep_src: src ali2 → (void| | - * | ali) — chain-direction DOWN | 0/0 - * chain_deep_var | kb95_deep_var: src base → (void| | - * | ali2) — chain-direction UP | 0/0 - * chain_call_bound81 | kb5_v2s1: CALL src 1-lvl — runs | - * | 0/0; byte-id waived: pre-existing | - * | #81 uninit-local zero-fill asm | - * | noise (NO at the #95 base too) | 0/0 - * chain_call2_bound81 | kb4_v2_struct2 (#95's original): | - * | CALL src 2-lvl — same #81 waiver | 0/0 - * chain_amb_loud | kb95_amb: (ali|ali2) over one base, | - * | src base — TODAY silently tagged | - * | member 0; ≥2 chain hits cannot be | - * | disambiguated in the nominal-lossy | - * | model (harec nassign≥2 → NULL) — | - * | hard-error, twin texts share the | - * | experr tail | err/err - * chain_amb_srcA/B | src ali / src ali2 into (ali|ali2): | - * | pass-1 EXACT fires first (harec P1 | - * | short-circuit) — precedence pins, | - * | MUST NOT MOVE | 0/0 - * nom_str / nom_err | (str|linerr) #218 nominal pins: str | - * | src tags str, linerr src tags | - * | linerr — MUST NOT MOVE through the | - * | new arm | 0/0 - * exact_ctl | kb5_v2sE2: exact-match ident | - * | control — MUST NOT MOVE | 0/0 - * bare_ctl/bare_2lvl | kb4_v2_ctrl / kb4_v2_alias2: pass-2 | - * | bare-source controls — MUST NOT | - * | MOVE | 0/0 - * bare_ambig/2 | kb4_v2_ambig(2): pass-2 ambiguity | - * | guards hold their loud texts | err/err - * callret_bound277 | kb5_v2sE: CALL src typed-as-alias — | - * | ww #277 aggregate-return capability | - * | leg, OUT of #95; cells pinned as | - * | observed (cs runs, ww loud) | 0/err - * unrel_struct | kb95_unrel (c2): ta/tb nominally- | - * | unrelated same-layout structs, src | - * | ta → (void|tb) — was both-wrong- | - * | identical byte-id; harec interning | - * | makes dealias(ta)==dealias(tb) → | - * | accept, tag tb (types.c:72-81 + | - * | :1000-1002) | 0/0 + * callret_bound277 (kb5_v2sE) CANNOT move: it is cstage-runs / wwstage-rejects + * -- cstage builds+runs it (exit 0) while wwstage LOUD-STOPS with "deferred + * #277" (the ww aggregate-return capability gap, OUT of #95). That asymmetry is + * inexpressible as a single-compile @test (which builds one way) or a runww + * //ww:error carrier (which demands BOTH stages reject). It stays a slim C pin. * - * K_RUN rows build+run BOTH drivers (exit==want) and assert cstage/ - * wwstage asm byte-id. K_RUN_NOID waives byte-id — cite the blocking - * task at the row. K_RUN_CS_WWERR: cstage build+run exit==cswant AND - * wwstage build FAILS with experr (944_alias_accept_run kind). NNN<950, - * self-contained /tmp sources, no imports (944 precedent). + * Non-vacuity (the pin CAN go RED): if cstage stops running it (exit != 0) or + * wwstage starts accepting it (#277 wired), the row fails; the wwstage leg also + * requires the EXACT "deferred #277" diagnostic so an unrelated wwstage error + * cannot masquerade as coverage. Mutation-checked: dropping the cast-to-alias + * (`mk()` typed plainly) flips cstage and was observed RED. */ #include #include @@ -86,36 +35,6 @@ runwait(const char *cmd) 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, exits==wants, + byte-id */ -#define K_BUILDERR 1 /* build FAILS with experr on BOTH drivers */ -#define K_RUN_NOID 2 /* build+run BOTH, exits==wants, byte-id - * SKIPPED — cite the blocking task */ -#define K_RUN_CS_WWERR 3 /* bound row: cstage build+run exit==cswant; - * wwstage build FAILS with experr — cite - * the blocking task */ - -struct row { const char *label; const char *src; - int cswant; int wwwant; int kind; const char *experr; }; - -/* errlog_has — a builderr cell must fail WITH its diagnostic; any other - * failure (parse error, crash, hang-kill) is a vacuous reject (940 - * precedent). */ static int errlog_has(const char *path, const char *needle) { @@ -128,390 +47,68 @@ errlog_has(const char *path, const char *needle) return strstr(buf, needle) != NULL; } -static const struct row rows[] = { - /* ---- c1: chain-membership graduations (were tag-0 silent) */ - { "chain_1lvl_i", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type al0 = base;\n" - "export fn main() i32 = {\n" - " let s: base;\n" - " s.a = 4; s.b = 9;\n" - " let v: (void | al0) = s;\n" - " if (!(v is al0)) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - { "chain_2lvl_i", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type al0 = base;\n" - "type ali = al0;\n" - "export fn main() i32 = {\n" - " let s: base;\n" - " s.a = 4; s.b = 9;\n" - " let v: (void | ali) = s;\n" - " if (!(v is ali)) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - { "chain_deep_src", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type ali = base;\n" - "type ali2 = ali;\n" - "export fn main() i32 = {\n" - " let s: ali2;\n" - " s.a = 4; s.b = 9;\n" - " let v: (void | ali) = s;\n" - " if (!(v is ali)) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - { "chain_deep_var", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type ali = base;\n" - "type ali2 = ali;\n" - "export fn main() i32 = {\n" - " let s: base;\n" - " s.a = 4; s.b = 9;\n" - " let v: (void | ali2) = s;\n" - " if (!(v is ali2)) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - /* CALL-src legs run 0/0; byte-id waived: the mk() frame carries - * the pre-existing #81 uninit-local zero-fill asm divergence - * (cells were byteid=NO at the #95 base too). */ - { "chain_call_bound81", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type al0 = base;\n" - "fn mk() base = {\n" - " let s: base;\n" - " s.a = 4; s.b = 9;\n" - " return s;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: (void | al0) = mk();\n" - " if (!(v is al0)) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN_NOID, NULL }, /* byte-id: task #81 */ - { "chain_call2_bound81", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type al0 = base;\n" - "type ali = al0;\n" - "fn mk() base = {\n" - " let s: base;\n" - " s.a = 4; s.b = 9;\n" - " return s;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: (void | ali) = mk();\n" - " if (!(v is ali)) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN_NOID, NULL }, /* byte-id: task #81 */ - /* ≥2 chain hits — was a SILENT member-0 tag; twin texts share - * the experr tail (prefix convention). */ - { "chain_amb_loud", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type ali = base;\n" - "type ali2 = ali;\n" - "export fn main() i32 = {\n" - " let s: base;\n" - " s.a = 4; s.b = 9;\n" - " let v: (ali | ali2) = s;\n" - " if (v is ali) { return 1; };\n" - " if (v is ali2) { return 2; };\n" - " return 3;\n" - "};\n", 0, 0, K_BUILDERR, - "source alias chain reaches >=2 variants" }, - /* pass-1 EXACT precedence pins: an exact source distinguishes - * (ali|ali2) — the chain arm must stay BEHIND pass 1. */ - { "chain_amb_srcA", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type ali = base;\n" - "type ali2 = ali;\n" - "export fn main() i32 = {\n" - " let s: ali;\n" - " s.a = 4; s.b = 9;\n" - " let v: (ali | ali2) = s;\n" - " if (!(v is ali)) { return 1; };\n" - " if (v is ali2) { return 2; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - { "chain_amb_srcB", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type ali = base;\n" - "type ali2 = ali;\n" - "export fn main() i32 = {\n" - " let s: ali2;\n" - " s.a = 4; s.b = 9;\n" - " let v: (ali | ali2) = s;\n" - " if (!(v is ali2)) { return 1; };\n" - " if (v is ali) { return 2; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - /* #218 nominal regression pins (rob §3e): (str | linerr) stays - * distinguishable through the new arm. */ - { "nom_str", - "package main;\n" - "type linerr = str;\n" - "export fn main() i32 = {\n" - " let s: str = \"ok\";\n" - " let v: (str | linerr) = s;\n" - " if (!(v is str)) { return 1; };\n" - " if (v is linerr) { return 2; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - { "nom_err", - "package main;\n" - "type linerr = str;\n" - "export fn main() i32 = {\n" - " let e: linerr = \"bad\";\n" - " let v: (str | linerr) = e;\n" - " if (!(v is linerr)) { return 1; };\n" - " if (v is str) { return 2; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - { "exact_ctl", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type al0 = base;\n" - "type ali = al0;\n" - "export fn main() i32 = {\n" - " let s: ali;\n" - " s.a = 4; s.b = 9;\n" - " let v: (void | ali) = s;\n" - " if (!(v is ali)) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - /* ---- pass-2 bare-source controls (the #15 leg stays put) */ - { "bare_ctl", - "package main;\n" - "type vt = struct { a: int };\n" - "type stream = *vt;\n" - "export fn main() i32 = {\n" - " let x: vt;\n" - " x.a = 7;\n" - " let v: (void | stream) = &x;\n" - " if (!(v is stream)) { return 1; };\n" - " match (v) {\n" - " case let s: stream => { if (s.a != 7) { return 2; }; };\n" - " case void => { return 3; };\n" - " };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - { "bare_2lvl", - "package main;\n" - "type vt = struct { a: int };\n" - "type st0 = *vt;\n" - "type stream = st0;\n" - "export fn main() i32 = {\n" - " let x: vt;\n" - " x.a = 7;\n" - " let v: (void | stream) = &x;\n" - " if (!(v is stream)) { return 1; };\n" - " match (v) {\n" - " case let s: stream => { if (s.a != 7) { return 2; }; };\n" - " case void => { return 3; };\n" - " };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - { "bare_ambig", - "package main;\n" - "type p1 = *i32;\n" - "type p2 = *i32;\n" - "export fn main() i32 = {\n" - " let x: i32 = 7;\n" - " let v: (p1 | p2) = &x;\n" - " return 0;\n" - "};\n", 0, 0, K_BUILDERR, - "bare source structurally matches >=2 NAMED variants" }, - { "bare_ambig2", - "package main;\n" - "type b1 = *i32;\n" - "type b2 = *i32;\n" - "type q1 = b1;\n" - "type q2 = b2;\n" - "export fn main() i32 = {\n" - " let x: i32 = 7;\n" - " let v: (q1 | q2) = &x;\n" - " return 0;\n" - "};\n", 0, 0, K_BUILDERR, - "bare source structurally matches >=2 NAMED variants" }, - /* ww #277 aggregate-return capability leg — OUT of #95; dual - * cells pinned as observed so neither side drifts silently. */ - { "callret_bound277", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type al0 = base;\n" - "type ali = al0;\n" - "fn mk() ali = {\n" - " let s: ali;\n" - " s.a = 4; s.b = 9;\n" - " return s;\n" - "};\n" - "export fn main() i32 = {\n" - " let v: (void | ali) = mk();\n" - " if (!(v is ali)) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN_CS_WWERR, - "deferred #277" }, /* ww: task #277 */ - /* ---- c2: structural fallback — was both-wrong-identical - * byte-id silent (live metric-1: checkers accept, cgen tagged - * 0). harec accepts by interning (one node, tag tb). */ - { "unrel_struct", - "package main;\n" - "type ta = struct { a: int, b: int };\n" - "type tb = struct { a: int, b: int };\n" - "export fn main() i32 = {\n" - " let s: ta;\n" - " s.a = 4; s.b = 9;\n" - " let v: (void | tb) = s;\n" - " if (!(v is tb)) { return 1; };\n" - " return 0;\n" - "};\n", 0, 0, K_RUN, NULL }, - /* ---- c3 (#107): is/as ACCEPTANCE must stay nominal-exact. cgen's - * chain/structural tag-synthesis arms widen tag lookup at the WIDEN - * site only; the wwstage is/as gate (check.ww:4677) reuses - * flatvariantidxt and, pre-c3, the widening leaked into ACCEPTANCE - * (cs!=ww) and surfaced the >=2 cgen fatal mid-check. c3 added an - * exact-only mode for that caller. These pin BOTH stages REJECTING - * (cstage's independent gate check.c:2036 already does; wwstage now - * matches it) — NOT a crash for the ambiguity shape. Shared experr - * substring "not a variant" (cstage "X is not a variant of (...)", - * wwstage "is/as: not a variant of operand (X)"). */ - { "isas_chain_reject", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type ali = base;\n" - "fn f(v: (void | ali)) i32 = {\n" - " if (v is base) { return 1; };\n" - " return 0;\n" - "};\n" - "export fn main() i32 = { return 0; };\n", - 0, 0, K_BUILDERR, "not a variant" }, - { "isas_unrel_reject", - "package main;\n" - "type ta = struct { a: int, b: int };\n" - "type tb = struct { a: int, b: int };\n" - "fn f(v: (void | tb)) i32 = {\n" - " if (v is ta) { return 1; };\n" - " return 0;\n" - "};\n" - "export fn main() i32 = { return 0; };\n", - 0, 0, K_BUILDERR, "not a variant" }, - { "isas_amb_reject_notcrash", - "package main;\n" - "type base = struct { a: int, b: int };\n" - "type ali = base;\n" - "type ali2 = ali;\n" - "fn f(v: (ali | ali2)) i32 = {\n" - " if (v is base) { return 1; };\n" - " return 0;\n" - "};\n" - "export fn main() i32 = { return 0; };\n", - 0, 0, K_BUILDERR, "not a variant" }, - /* ---- rob's obligated mixed prim/alias TWIN: union (int | ai) - * ai=int, source typed aj=int — under all-variants counting both the - * bare `int` variant and `ai` share the int bottom -> the cgen widen - * (full mode) hard-errors (harec nassign>=2 -> NULL). Pinned LOUD - * BOTH stages (the chain-ambiguity path; #95 deviation-2 ratified). */ - { "twin_prim_alias_amb", - "package main;\n" - "type ai = int;\n" - "type aj = int;\n" - "export fn main() i32 = {\n" - " let s: aj = 7;\n" - " let v: (int | ai) = s;\n" - " return 0;\n" - "};\n", 0, 0, K_BUILDERR, - "source alias chain reaches >=2 variants" }, -}; +/* kb5_v2sE: a CALL whose result is typed as the alias ali, widened into + * (void | ali). cstage's aggregate-return path runs it; wwstage #277 rejects. */ +static const char *src = + "package main;\n" + "type base = struct { a: int, b: int };\n" + "type al0 = base;\n" + "type ali = al0;\n" + "fn mk() ali = {\n" + " let s: ali;\n" + " s.a = 4; s.b = 9;\n" + " return s;\n" + "};\n" + "export fn main() i32 = {\n" + " let v: (void | ali) = mk();\n" + " if (!(v is ali)) { return 1; };\n" + " return 0;\n" + "};\n"; +static const char *experr = "deferred #277"; + +/* Build src with `driver`; if expect_err, the build must FAIL with experr on + * stderr. Otherwise build must succeed, then the binary runs and its exit must + * equal `want`. Returns 0 on the expected outcome, 1 otherwise. */ static int -run_driver(const char *driver, const struct row *r, int i, int expect_err, - int want) +check(const char *driver, int expect_err, int want) { - char tmpdir[96], src[128], outbin[128], errf[128], rmcmd[160]; - char cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/vc95_%d_d_%d", getpid(), i); + char tmpdir[96], srcp[128], outbin[128], errf[128], rmcmd[160], cmd[1024]; + snprintf(tmpdir, sizeof tmpdir, "/tmp/vc277_%d_%d", getpid(), expect_err); mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/vc95_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/vc95_%d_%d", tmpdir, getpid(), i); + snprintf(srcp, sizeof srcp, "%s/c.ww", tmpdir); + snprintf(outbin, sizeof outbin, "%s/c", tmpdir); 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); + FILE *f = fopen(srcp, "wb"); + if (!f) { runwait(rmcmd); return 1; } + fputs(src, f); fclose(f); - snprintf(cmd, sizeof cmd, - "timeout 20 %s build -o %s %s >/dev/null 2>%s", - driver, outbin, src, errf); + snprintf(cmd, sizeof cmd, "timeout 20 %s build -o %s %s >/dev/null 2>%s", + driver, outbin, srcp, errf); int brc = runwait(cmd); + + int rc = 0; if (expect_err) { - 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 || !errlog_has(errf, experr)) { + fprintf(stderr, "callret_bound277: %s expected loud " + "builderr \"%s\" (brc=%d)\n", driver, experr, brc); + rc = 1; + } + } else if (brc != 0) { + fprintf(stderr, "callret_bound277: build via %s failed\n", driver); + rc = 1; + } else { + int got = runwait(outbin); + if (got != want) { + fprintf(stderr, "callret_bound277: %s exit %d, want %d\n", + driver, got, want); + rc = 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 != want) { - fprintf(stderr, "row[%s]: %s exit %d, want %d\n", - r->label, driver, got, 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/vc95_asm_%d_%d.ww", getpid(), i); - snprintf(cs, sizeof cs, "/tmp/vc95_asm_%d_%d_c.s", getpid(), i); - snprintf(ws, sizeof ws, "/tmp/vc95_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; } @@ -532,36 +129,15 @@ main(void) 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++; - int cs_err = rows[i].kind == K_BUILDERR; - if (run_driver(cdrv, &rows[i], i, cs_err, - rows[i].cswant) != 0) fail++; - } - if (access(wdrv, X_OK) == 0) { - for (int i = 0; i < n; i++) { - total++; - int ww_err = rows[i].kind == K_BUILDERR - || rows[i].kind == K_RUN_CS_WWERR; - if (run_driver(wdrv, &rows[i], i, ww_err, - rows[i].wwwant) != 0) fail++; - } - for (int i = 0; i < n; i++) { - if (rows[i].kind != K_RUN) - continue; - total++; - if (asm_byte_identical(bin, &rows[i], i) != 0) fail++; - } - } + int fail = 0; + fail += check(cdrv, 0, 0); /* cstage runs, exit 0 */ + if (access(wdrv, X_OK) == 0) + fail += check(wdrv, 1, 0); /* wwstage loud-stops #277 */ if (fail) { - fprintf(stderr, "variant_chain_b95: %d/%d checks failed\n", - fail, total); + fprintf(stderr, "variant_chain_b95: %d checks failed\n", fail); return 1; } - printf("variant_chain_b95: %d/%d ok\n", total, total); + printf("variant_chain_b95: callret_bound277 pin ok\n"); return 0; } diff --git a/test/wcc/949_void_error_singleton_run.c b/test/wcc/949_void_error_singleton_run.c deleted file mode 100644 index ebe94b05..00000000 --- a/test/wcc/949_void_error_singleton_run.c +++ /dev/null @@ -1,287 +0,0 @@ -/* - * 949_void_error_singleton_run — #140: a `!void` error-singleton used - * as a VALUE is a tag-only variant. Its void payload has size 0 and no - * storage, so cgexpr must materialise NOTHING — only the enclosing - * widen arm stamps the variant tag. - * - * Pre-fix cstage's cgexpr N_IDENT path fell through to the generic - * global-value load and emitted `MOVQ main.(SB), AX` for a - * payload symbol that is never defined: - * w6l: undefined reference to 'main.too_long' - * wwstage already emitted tag-only (no symbol load) — the runtime- - * correct reference. The fix aligns cstage UP (ken-139 oracle). - * - * Every singleton-value position the cgexpr-ident root feeds is - * covered (enumerated at HEAD — all four link-failed identically): - * return, let-init, assign, call-arg. Rows also vary the union width - * (8B scalar payload vs a >8B str payload, which exercises the - * tag-return CX/R8 zeroing) and check BOTH the error-arm and the - * success-arm tags so a mis-stamped tag flips the exit code. - * - * Three checks per row: - * - byte-id: w6c vs w6c_ww .s must be identical (rule 10). - * - no-bogus-load: the cstage .s must NOT reference `(SB)` - * for the singleton type-name (the pre-fix undefined symbol). - * - runtime: build via the ww / ww_ww drivers (the build step is - * the link, which is the pre-fix teeth) and run; the exit code - * pins the variant tag. - * - * This is half of the path-c1 pin (union with #141's def-dim file). - */ -#include -#include -#include -#include -#include -#include - -static int -runwait(const char *cmd) -{ - int rc = system(cmd); - if (rc == -1) return -1; - if (WIFEXITED(rc)) return WEXITSTATUS(rc); - return -1; -} - -struct row { - const char *label; - const char *src; - int want; /* expected exit code */ -}; - -static const struct row rows[] = { - /* return-position: the void singleton returned from an 8B-payload - * union; match selects the singleton arm (exit 0). */ - { "return_singleton", - "type too_long = !void;\n" - "fn f() (i64 | too_long) = {\n" - " return too_long;\n" - "};\n" - "export fn main() i32 = {\n" - " match (f()) {\n" - " case let v: i64 => return 1;\n" - " case too_long => return 0;\n" - " };\n" - " return 2;\n" - "};\n", - 0 }, - /* same fn shape, success variant: pins the tag discriminates - * (the success arm must run, returning the payload). */ - { "return_success", - "type too_long = !void;\n" - "fn f() (i64 | too_long) = {\n" - " return 42i64;\n" - "};\n" - "export fn main() i32 = {\n" - " match (f()) {\n" - " case let v: i64 => { if (v != 42) { return 3; }; return 0; };\n" - " case too_long => return 1;\n" - " };\n" - " return 2;\n" - "};\n", - 0 }, - /* let-init: `let e: (i64 | too_long) = too_long;`. */ - { "letinit_singleton", - "type too_long = !void;\n" - "export fn main() i32 = {\n" - " let e: (i64 | too_long) = too_long;\n" - " match (e) {\n" - " case let v: i64 => return 1;\n" - " case too_long => return 0;\n" - " };\n" - " return 2;\n" - "};\n", - 0 }, - /* assign: reassign an existing union local to the void singleton - * (cg_widen_tagged_store source = the singleton ident). */ - { "assign_singleton", - "type too_long = !void;\n" - "export fn main() i32 = {\n" - " let e: (i64 | too_long) = 5i64;\n" - " e = too_long;\n" - " match (e) {\n" - " case let v: i64 => return 1;\n" - " case too_long => return 0;\n" - " };\n" - " return 2;\n" - "};\n", - 0 }, - /* call-arg: the singleton passed into a tagged parameter. */ - { "callarg_singleton", - "type too_long = !void;\n" - "fn use(r: (i64 | too_long)) i32 = {\n" - " match (r) {\n" - " case let v: i64 => return 1;\n" - " case too_long => return 0;\n" - " };\n" - " return 2;\n" - "};\n" - "export fn main() i32 = {\n" - " return use(too_long);\n" - "};\n", - 0 }, - /* wide union: a >8B (str) success payload makes the tagged return - * slot exceed one word, exercising the CX/R8 zeroing on the - * tag-only singleton return. Error arm selected. */ - { "return_singleton_wide", - "type too_long = !void;\n" - "fn f(ok: bool) (str | too_long) = {\n" - " if (ok) { return \"hello\"; };\n" - " return too_long;\n" - "};\n" - "export fn main() i32 = {\n" - " match (f(false)) {\n" - " case let s: str => return 1;\n" - " case too_long => { };\n" - " };\n" - " match (f(true)) {\n" - " case let s: str => { if (s.len != 5) { return 2; }; };\n" - " case too_long => return 3;\n" - " };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -static const char *g_bin; - -static int -compile_s(const char *tool, const char *src, const char *outpath, - const char *errpath) -{ - char cmd[1024]; - snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2> %s", - g_bin, tool, src, outpath, errpath); - return runwait(cmd); -} - -static int -file_eq(const char *a, const char *b) -{ - char cmd[1024]; - snprintf(cmd, sizeof cmd, "cmp -s %s %s", a, b); - return runwait(cmd) == 0; -} - -static int -file_has(const char *path, const char *needle) -{ - FILE *f = fopen(path, "rb"); - if (!f) return 0; - static char buf[1 << 20]; - size_t n = fread(buf, 1, sizeof buf - 1, f); - fclose(f); - buf[n] = '\0'; - return strstr(buf, needle) != NULL; -} - -static int -run_driver(const char *driver, const char *tmpdir, const char *src, - const char *label) -{ - char cmd[1024], outbin[256]; - snprintf(outbin, sizeof outbin, "%s/out_%s", tmpdir, driver); - snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1", - g_bin, driver, outbin, src); - if (runwait(cmd) != 0) { - fprintf(stderr, "row[%s]: build via %s failed\n", - label, driver); - return -1; - } - return runwait(outbin); -} - -int -main(void) -{ - const char *bin = getenv("BIN"); - if (!bin) bin = "out/bin"; - static 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; - } - g_bin = bin; - - int n = (int)(sizeof rows / sizeof rows[0]); - int total = 0, fail = 0; - for (int i = 0; i < n; i++) { - const struct row *r = &rows[i]; - char tmpdir[128], rmcmd[160]; - char src[160], cs_s[160], ww_s[160]; - char cs_e[160], ww_e[160]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/tves_%d_d_%d", - getpid(), i); - mkdir(tmpdir, 0755); - snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir); - snprintf(src, sizeof src, "%s/tves_%d_%d.ww", - tmpdir, getpid(), i); - snprintf(cs_s, sizeof cs_s, "%s/tves_%d_%d_cs.s", - tmpdir, getpid(), i); - snprintf(ww_s, sizeof ww_s, "%s/tves_%d_%d_ww.s", - tmpdir, getpid(), i); - snprintf(cs_e, sizeof cs_e, "%s/tves_%d_%d_cs.err", - tmpdir, getpid(), i); - snprintf(ww_e, sizeof ww_e, "%s/tves_%d_%d_ww.err", - tmpdir, getpid(), i); - FILE *f = fopen(src, "wb"); - if (!f) { runwait(rmcmd); return 1; } - fputs("package main;\n\n", f); - fputs(r->src, f); - fclose(f); - - total++; - int cs_rc = compile_s("w6c", src, cs_s, cs_e); - int ww_rc = compile_s("w6c_ww", src, ww_s, ww_e); - if (cs_rc != 0 || ww_rc != 0) { - fprintf(stderr, "FAIL row[%s]: compile rc cs=%d " - "ww=%d\n", r->label, cs_rc, ww_rc); - fail++; - runwait(rmcmd); - continue; - } - if (!file_eq(cs_s, ww_s)) { - fprintf(stderr, "FAIL row[%s]: cs != ww .s\n", - r->label); - fail++; - } - /* the pre-fix undefined-symbol signature: a `(SB)` load of - * the singleton type-name. Both unions name the singleton - * `too_long`; the wide row also has it. No row legitimately - * loads a `too_long(SB)` value (it is a type, not a global). */ - if (file_has(cs_s, "too_long(SB)")) { - fprintf(stderr, "FAIL row[%s]: cstage still loads " - "the nonexistent void-singleton payload symbol\n", - r->label); - fail++; - } - int got_cs = run_driver("ww", tmpdir, src, r->label); - if (got_cs != r->want) { - fprintf(stderr, "FAIL row[%s] cstage: want %d " - "got %d\n", r->label, r->want, got_cs); - fail++; - } - char wwdrv[600]; - snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin); - if (access(wwdrv, X_OK) == 0) { - int got_ww = run_driver("ww_ww", tmpdir, src, r->label); - if (got_ww != r->want) { - fprintf(stderr, "FAIL row[%s] wwstage: " - "want %d got %d\n", - r->label, r->want, got_ww); - fail++; - } - } - runwait(rmcmd); - } - if (fail) { - fprintf(stderr, "void_error_singleton_run: %d/%d rows " - "failed\n", fail, total); - return 1; - } - printf("void_error_singleton_run: %d rows ok\n", total); - return 0; -} diff --git a/test/wcc/989_globstructwiden_run.c b/test/wcc/989_globstructwiden_run.c deleted file mode 100644 index abb1b5ed..00000000 --- a/test/wcc/989_globstructwiden_run.c +++ /dev/null @@ -1,153 +0,0 @@ -/* - * 989_globstructwiden_run — F8-c7 (report-item #50, #263): widening a module- - * GLOBAL struct ident into a tagged union (`let x: (S | str) = gs;`) must - * copy gs's full payload into the box, not just word0. - * - * THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages): - * the struct-payload widen arm keyed off a local/literal-only struct-name - * lookup, so a module-global struct ident fell to the scalar word0 arm — only - * the first 8 bytes reached the box (wwstage), or the payload zero-filled - * (cstage, gs never read). The ww half landed in F8-c7; the cstage half (a - * global-struct-source branch — LEAQ g(SB),SI via aggarg_srcaddr, byte-copy - * into slot+8) landed in ww-core TASK #43. Both stages now copy the full - * payload and the .s is byte-identical. - * - * Rows assert the field-equality match returns 0 on both stages (cs==ww). - * row | shape | exit - * -----------+---------------------------------------------+----- - * widen_16 | gp:{a=5,b=6}; (pt|str)=gp; match pt fields | 0 - * widen_24 | gp:{a=5,b=6,c=7}; (tri|str)=gp; match fields | 0 - */ -#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_cs; - int want_ww; -}; - -static const struct row rows[] = { - { "widen_16", - "package main;\n" - "type pt = struct { a: i64, b: i64 };\n" - "let gp: pt = pt { a = 5, b = 6 };\n" - "export fn main() int = {\n" - " let x: (pt | str) = gp;\n" - " match (x) {\n" - " case let q: pt => { if (q.a == 5 && q.b == 6) { return 0; }; return 2; };\n" - " case str => return 3;\n" - " };\n" - "};\n", - 0, 0 }, - - { "widen_24", - "package main;\n" - "type tri = struct { a: i64, b: i64, c: i64 };\n" - "let gp: tri = tri { a = 5, b = 6, c = 7 };\n" - "export fn main() int = {\n" - " let x: (tri | str) = gp;\n" - " match (x) {\n" - " case let q: tri => { if (q.a == 5 && q.b == 6 && q.c == 7) { return 0; }; return 2; };\n" - " case str => return 3;\n" - " };\n" - "};\n", - 0, 0 }, -}; - -/* 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 src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/gsw_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/gsw_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/gsw_%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); - - struct { const char *name; const char *drv; 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].drv, X_OK) != 0) { - fprintf(stderr, "globstructwiden_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].drv); - continue; - } - int is_ww = (d == 1); - for (int i = 0; i < n; i++) { - total++; - int want = is_ww ? rows[i].want_ww : rows[i].want_cs; - int got = run_build(drivers[d].drv, &rows[i], i); - if (got != want) { - fprintf(stderr, "globstructwiden_run[%s][%s]: exit=%d " - "want=%d\n", drivers[d].name, rows[i].label, - got, want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "globstructwiden_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("globstructwiden_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/989_globtagisas_run.c b/test/wcc/989_globtagisas_run.c deleted file mode 100644 index 64158320..00000000 --- a/test/wcc/989_globtagisas_run.c +++ /dev/null @@ -1,170 +0,0 @@ -/* - * 989_globtagisas_run — F8-c3 (report-item #18): `is`/`as` on a module- - * GLOBAL tagged-union ident must read the tag/payload from g(SB), not the - * saved-BP word. - * - * THE BUG (cat-A silent miscompile): in cgenexpr.ww cgtypetest (`is`) and - * cgtypeassert (`as`) resolve an N_IDENT scrutinee's slot via localfindnode. - * A module-global ident returns nil (no BP slot), so scrutoff stayed 0 and - * the tag read fell on 0(BP) = the saved-BP word (and the payload on 8(BP) = - * the return address). cstage N_TYPETEST is uniformly cgexpr (MOVQ g(SB),AX), - * so the bootstrap corpus never trips it on 990-997 — a runtime row is the net. - * - * MIXED CATEGORY (per-half, asm-verified — spec §3 principle): - * • `is` half = ALIGN-UP. cstage reads the tag correctly (MOVQ g(SB),AX); - * wwstage read 0(BP). FIX routes the global ident to MOVQ g(SB),AX → - * cs==ww + runtime-correct (the is_* rows assert want on BOTH drivers). - * • `as` half = #263 BOTH-WRONG, now CLOSED both stages. cstage N_TYPEASSERT - * on a global ident spilled cgexpr's AX (tag only) plus UNINITIALIZED DX as - * the payload (never loaded g(SB)+8) → garbage (0). The ww half landed in - * F8-c3; the cstage half (copy the box words from g(SB)+0/+8[/+16] into the - * @asrt_spill so the tag-check + payload load index off memory like a local) - * landed in ww-core TASK #46. Both stages now read the real payload and the - * .s is byte-identical. - * - * Rows (built+run on cstage `ww` and wwstage `ww_ww`; cs==ww on every row): - * row | shape | exit | cat - * ----------+----------------------------------------+------+-------- - * is_match | g:(i64|bool)=5; if g is i64 →0 else 12 | 0 | align-UP - * is_nomatch| g:(i64|bool)=5; if g is bool →13 else 0 | 0 | align-UP - * as_i64 | g:(i64|bool)=42; (g as i64):int | 42 | #46 - * as_str | g:(str|i64)="hello"; len(g as str) | 5 | #46 - */ -#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_cs; - int want_ww; -}; - -static const struct row rows[] = { - { "is_match", - "package main;\n" - "let g: (i64 | bool) = 5;\n" - "fn check() int = { if (g is i64) { return 0; }; return 12; };\n" - "export fn main() int = { return check(); };\n", - 0, 0 }, - - { "is_nomatch", - "package main;\n" - "let g: (i64 | bool) = 5;\n" - "fn check() int = { if (g is bool) { return 13; }; return 0; };\n" - "export fn main() int = { return check(); };\n", - 0, 0 }, - - /* #46 CLOSED both stages: cstage now copies the box from g(SB) into the - * @asrt_spill (was spilling uninitialized DX) → 42 == wwstage. */ - { "as_i64", - "package main;\n" - "let g: (i64 | bool) = 42;\n" - "fn check() int = { let x: i64 = g as i64; return x: int; };\n" - "export fn main() int = { return check(); };\n", - 42, 42 }, - - /* #46 str variant — both stages copy the +16 cap word too. */ - { "as_str", - "package main;\n" - "let g: (str | i64) = \"hello\";\n" - "fn check() int = { let s: str = g as str; return len(s): int; };\n" - "export fn main() int = { return check(); };\n", - 5, 5 }, -}; - -/* 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/gtia_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/gtia_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/gtia_%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); - - struct { const char *name; const char *drv; 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].drv, X_OK) != 0) { - fprintf(stderr, "globtagisas_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].drv); - continue; - } - int is_ww = (d == 1); - for (int i = 0; i < n; i++) { - total++; - int want = is_ww ? rows[i].want_ww : rows[i].want_cs; - int got = run_build(drivers[d].drv, &rows[i], i); - if (got != want) { - fprintf(stderr, "globtagisas_run[%s][%s]: exit=%d " - "want=%d\n", drivers[d].name, rows[i].label, - got, want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "globtagisas_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("globtagisas_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/989_globtagreassign_run.c b/test/wcc/989_globtagreassign_run.c deleted file mode 100644 index 4822aaae..00000000 --- a/test/wcc/989_globtagreassign_run.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 989_globtagreassign_run — F8-c5 (report-item #32, #263): reassigning a - * module-GLOBAL tagged-union ident (`g = expr`) must store the new tag and - * payload, not clobber only one word. - * - * THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages): - * cgassign's tagged-union reassignment arm delegated to the widener ONLY for a - * LOCAL ident. A module-global tagged ident had no arm and fell through to the - * generic scalar store (wwstage clobbered the tag word; cstage dropped the - * store entirely). The ww half landed in F8-c5; the cstage half (LEAQ g(SB),BX - * then cg_widen_tagged_store off BX) landed in ww-core TASK #41 — both stages - * now store the full box and the .s is byte-identical. - * - * Rows (cs==ww after the cstage half landed): - * row | shape | exit - * ----------------+----------------------------------------+----- - * reassign_i64 | g:(i64|void)=0; g=7; match i64 | 7 - * reassign_str | g:(str|i64)=0; g="hello"; match str→len | 5 - * reassign_union | g:(i64|bool)=0; g=5; match i64 | 5 - */ -#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_cs; - int want_ww; -}; - -static const struct row rows[] = { - { "reassign_i64", - "package main;\n" - "let g: (i64 | void) = 0;\n" - "export fn main() int = {\n" - " g = 7;\n" - " match (g) {\n" - " case let n: i64 => return n: int;\n" - " case void => return 99;\n" - " };\n" - "};\n", - 7, 7 }, - - { "reassign_str", - "package main;\n" - "let g: (str | i64) = 0;\n" - "export fn main() int = {\n" - " g = \"hello\";\n" - " match (g) {\n" - " case let s: str => return len(s): int;\n" - " case i64 => return 88;\n" - " };\n" - "};\n", - 5, 5 }, - - { "reassign_union", - "package main;\n" - "let g: (i64 | bool) = 0;\n" - "export fn main() int = {\n" - " g = 5;\n" - " match (g) {\n" - " case let n: i64 => return n: int;\n" - " case bool => return 77;\n" - " };\n" - "};\n", - 5, 5 }, -}; - -/* 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/gtr_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/gtr_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/gtr_%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); - - struct { const char *name; const char *drv; 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].drv, X_OK) != 0) { - fprintf(stderr, "globtagreassign_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].drv); - continue; - } - int is_ww = (d == 1); - for (int i = 0; i < n; i++) { - total++; - int want = is_ww ? rows[i].want_ww : rows[i].want_cs; - int got = run_build(drivers[d].drv, &rows[i], i); - if (got != want) { - fprintf(stderr, "globtagreassign_run[%s][%s]: exit=%d " - "want=%d\n", drivers[d].name, rows[i].label, - got, want); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "globtagreassign_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("globtagreassign_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/989_globtagwiden_run.c b/test/wcc/989_globtagwiden_run.c deleted file mode 100644 index 140cfe2e..00000000 --- a/test/wcc/989_globtagwiden_run.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * 989_globtagwiden_run — F8-c8 (report-item #51, #263): widening a module- - * GLOBAL tagged-union ident into a wider union (`let x: (inner | str) = gi;`) - * must copy gi's whole box as the nested payload, not store its tag as the - * payload. - * - * THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages): - * the #218 nested-tagged-source arm was gated local-only, so a module-global - * tagged source fell to the scalar word0 widen arm — gi's TAG word landed as - * the box payload (wwstage), or frame garbage was copied, never gi(SB) - * (cstage). The ww half landed in F8-c8; the cstage half (the nested arm's - * N_IDENT copy grows a global branch — LEAQ gi(SB),SI via aggarg_srcaddr, copy - * the inner box into slot+8) landed in ww-core TASK #44. Both stages now copy - * the whole box and the .s is byte-identical. - * - * Rows assert the exact runtime-correct value on both stages (cs==ww). - * row | shape | exit - * ----------+-----------------------------------------+----- - * nest_47 | gi:inner=47; (inner|str)=gi; match→v | 47 - * nest_99 | gi:inner=99; (inner|str)=gi; match→v | 99 - */ -#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_ww; -}; - -static const struct row rows[] = { - { "nest_47", - "package main;\n" - "type inner = (i64 | bool);\n" - "let gi: inner = 47;\n" - "export fn main() int = {\n" - " let x: (inner | str) = gi;\n" - " match (x) {\n" - " case let n: inner => {\n" - " match (n) {\n" - " case let v: i64 => return v: int;\n" - " case bool => return 88;\n" - " };\n" - " };\n" - " case str => return 3;\n" - " };\n" - "};\n", - 47 }, - - { "nest_99", - "package main;\n" - "type inner = (i64 | bool);\n" - "let gi: inner = 99;\n" - "export fn main() int = {\n" - " let x: (inner | str) = gi;\n" - " match (x) {\n" - " case let n: inner => {\n" - " match (n) {\n" - " case let v: i64 => return v: int;\n" - " case bool => return 88;\n" - " };\n" - " };\n" - " case str => return 3;\n" - " };\n" - "};\n", - 99 }, -}; - -/* 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 src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/gtw_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/gtw_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/gtw_%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); - - struct { const char *name; const char *drv; 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].drv, X_OK) != 0) { - fprintf(stderr, "globtagwiden_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].drv); - continue; - } - for (int i = 0; i < n; i++) { - total++; - int got = run_build(drivers[d].drv, &rows[i], i); - /* #44 CLOSED: cs==ww at the exact runtime-correct value - * on both drivers (the .s is byte-identical). */ - if (got != rows[i].want_ww) { - fprintf(stderr, "globtagwiden_run[%s][%s]:" - " exit=%d want=%d\n", drivers[d].name, - rows[i].label, got, rows[i].want_ww); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "globtagwiden_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("globtagwiden_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/989_taggedderefstore_run.c b/test/wcc/989_taggedderefstore_run.c deleted file mode 100644 index 018518f0..00000000 --- a/test/wcc/989_taggedderefstore_run.c +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 989_taggedderefstore_run — #17 teeth (recorded by impl-write, 2026-06-13). - * - * Deref-target store into a tagged-union pointee: `*p = v` where p:*tagged. - * The plain-deref store arm sized the write off the pointee type and emitted - * a SINGLE fldstoreop (one MOVQ) — rhs landed in the tag word, the payload - * was dropped, and the union's discriminant was corrupted. A subsequent - * `match (v)` then dispatched on garbage. - * - * THIS WAS BOTH-STAGE-WRONG AND BYTE-ID-GREEN (#263-class, gate-blind): the - * cstage (cmd/w6c/cgen.c #17 deref else-branch) and wwstage (cgenexpr.ww - * #17 single-store tail) emitted IDENTICAL wrong asm, so the byte-id gates - * never caught it. #17 routes both stages' tagged-pointee deref store - * through the widener (cstage cg_widen_tagged_store / ww cgwidentaggedstore) - * via the shared tag scratch, then word-copies scratch -> *p — mirroring the - * proven index-element tagged arm (cstage cgen.c:6487 / ww cgenexpr.ww:8768). - * Scalar pointees keep the single-store path untouched. - * - * Rows (each program self-checks and returns 0 on all-correct, a 1-based - * code on the first mismatch; both stages build+run, rule-10, pin 0): - * taglo: *(int|bool), *p=42 — tag-low (int) variant, read back 42. - * taghi: *(int|bool), *p=true — tag-high (bool) variant survives. - * str3w: *(int|str), *p="hello" — a str payload is a 3-word {ptr,len, - * cap} header (slot = tag + 3 payload words); proves the - * multi-payload-word copy is COMPLETE, not just word0/word1. - * struct2w: *(int|pair), *p=pair{...} — a 16B struct payload (slot = tag + - * 2 payload words, ssz 24); proves the mid-count copy loop moves - * BOTH payload words and the widener's struct-source arm fires. - * nullfold: *(*int|void), *p=&a — nullable fold (1-word slot, pointer IS - * the disc); the folded payload path widens correctly. - * scalarctl:*int, *p=42 — scalar control: the non-tagged single-store - * path is UNCHANGED (no scratch, no widen). - */ -#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: pin the absolute value; -1: cs==ww only */ -}; - -static const struct row rows[] = { - /* taglo — tag-low (int) variant via *p=42; read back the int. */ - { "taglo", - "package main;\n" - "export fn main() int = {\n" - " let v: (int | bool) = false;\n" - " let p: *(int | bool) = &v;\n" - " *p = 42;\n" - " match (v) {\n" - " case let n: int => { if (n != 42) { return 1; }; return 0; };\n" - " case bool => { return 2; };\n" - " };\n" - "};\n", - 0 }, - - /* taghi — tag-high (bool) variant via *p=true; the tag must remap. */ - { "taghi", - "package main;\n" - "export fn main() int = {\n" - " let v: (int | bool) = 0;\n" - " let p: *(int | bool) = &v;\n" - " *p = true;\n" - " match (v) {\n" - " case int => { return 1; };\n" - " case let b: bool => { if (!b) { return 2; }; return 0; };\n" - " };\n" - "};\n", - 0 }, - - /* str3w — str payload (3-word header); the >2-word payload copy must - * be complete. Pre-fix only the tag word (or one payload word) moved. */ - { "str3w", - "package main;\n" - "export fn main() int = {\n" - " let v: (int | str) = 0;\n" - " let p: *(int | str) = &v;\n" - " *p = \"hello\";\n" - " match (v) {\n" - " case int => { return 1; };\n" - " case let s: str => { if (s.len: int != 5) { return 2; }; return 0; };\n" - " };\n" - "};\n", - 0 }, - - /* struct2w — an aggregate (struct) payload: a 16B pair → slot = tag + - * 2 payload words (ssz 24, 3-word copy loop). Exercises the widener's - * struct-source arm through the deref path and proves the mid-count - * copy loop moves BOTH payload words, not word0 alone. */ - { "struct2w", - "package main;\n" - "type pair = struct { a: i64, b: i64 };\n" - "export fn main() int = {\n" - " let v: (int | pair) = 0;\n" - " let p: *(int | pair) = &v;\n" - " *p = pair { a = 0x1111, b = 0x2222 };\n" - " match (v) {\n" - " case int => { return 1; };\n" - " case let q: pair => {\n" - " if (q.a != 0x1111) { return 2; };\n" - " if (q.b != 0x2222) { return 3; };\n" - " return 0;\n" - " };\n" - " };\n" - "};\n", - 0 }, - - /* nullfold — nullable fold (1-word slot): the pointer is the disc. */ - { "nullfold", - "package main;\n" - "export fn main() int = {\n" - " let a: int = 7;\n" - " let v: (*int | void) = void;\n" - " let p: *(*int | void) = &v;\n" - " *p = &a;\n" - " match (v) {\n" - " case let q: *int => { if (*q != 7) { return 1; }; return 0; };\n" - " case void => { return 2; };\n" - " };\n" - "};\n", - 0 }, - - /* scalarctl — non-tagged pointee: the single-store path is unchanged. */ - { "scalarctl", - "package main;\n" - "export fn main() int = {\n" - " let v: int = 3;\n" - " let p: *int = &v;\n" - " *p = 42;\n" - " if (v != 42) { return 1; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -/* 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], cmd[1024], rmcmd[160]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/tagderef_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tagderef_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tagderef_%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, "taggedderefstore_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, "taggedderefstore_run[cstage][%s]: exit=%d " - "want=%d (tag corrupt / payload dropped)\n", - rows[i].label, gc, rows[i].want_exit); - fail++; - } - if (!have_ww) { - fprintf(stderr, "taggedderefstore_run: skip wwstage (no %s)\n", - wdrv); - continue; - } - int gw = run_build(wdrv, &rows[i], i); - if (gw != gc) { - fprintf(stderr, "taggedderefstore_run[%s]: cs=%d != ww=%d " - "(#17 deref-store divergence)\n", - rows[i].label, gc, gw); - fail++; - } - if (rows[i].want_exit >= 0 && gw != rows[i].want_exit) { - fprintf(stderr, "taggedderefstore_run[wwstage][%s]: exit=%d " - "want=%d (tag corrupt / payload dropped)\n", - rows[i].label, gw, rows[i].want_exit); - fail++; - } - } - - if (fail) { - fprintf(stderr, "taggedderefstore_run: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("taggedderefstore_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/989_taggedglobalindex_run.c b/test/wcc/989_taggedglobalindex_run.c deleted file mode 100644 index dae97e76..00000000 --- a/test/wcc/989_taggedglobalindex_run.c +++ /dev/null @@ -1,248 +0,0 @@ -/* - * 989_taggedglobalindex_run — #16 teeth (recorded by impl-16, 2026-06-14). - * - * Indexed store into a GLOBAL array of a tagged-union type: `gs[i] = v` where - * gs : [N](A | B). The indexed-tagged-element assign arm (cmd/w6c/cgen.c - * ~6502) computed its base address with only LOCAL/frame variants — it LACKED - * the `isglobal -> LEAQ name(SB)` branch the SCALAR `arr[i] = v` element arm - * (~6767) already had. For a global tagged array the base resolved to a junk - * BP-relative offset (off==0), so the widened store landed nowhere observable; - * a later `match (gs[i])` then read the unchanged static-init slot. cstage - * silently returned 0 (WRONG); wwstage (cgenexpr.ww index arm, already carries - * the globalname branch) returned the correct value — a cs!=ww divergence the - * byte-id gate could SEE. #16 aligns cstage UP: it adds the mirrored - * `isglobal && is_arr -> LEAQ name(SB)` / `isglobal -> MOVQ name(SB)` branch to - * the indexed-tagged-elem arm. cstage-only; no .ww source touched. - * - * Rows (each program self-checks and returns 0 on all-correct, a 1-based code - * on the first mismatch; both stages build+run, rule-10, pin 0): - * glo_taglo: global [N](int|bool), gs[1]=42 — tag-low (int) variant, read - * back 42. The literal #16 teeth (cstage pre-fix gave 0). - * glo_taghi: global [N](int|bool), gs[1]=true — tag-high (bool) variant; - * the tag must remap through the widener. - * glo_varidx: global [N](int|bool), runtime index gs[i]=99 — the scaled- - * index path (IMULQ) layered on the global base. - * glo_str3w: global [N](int|str), gs[1]="hello" — a str payload is a - * 3-word {ptr,len,cap} header; proves the multi-word slot copy - * over the global base is COMPLETE, not just word0/word1. - * glo_struct2w:global [N](int|pair), gs[1]=pair{...} — a 16B struct payload - * (slot = tag + 2 payload words); proves the mid-count copy loop - * moves BOTH payload words over the global base. - * loc_ctl: LOCAL [N](int|bool), a[1]=42 — control for the frame-base - * path that ALREADY worked; must stay correct + byte-id. - * scalar_ctl: scalar global [N]int, arr[1]=42 — control for the template - * arm (the scalar element store) the fix was mirrored from. - */ -#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: pin the absolute value; -1: cs==ww only */ -}; - -static const struct row rows[] = { - /* glo_taglo — the #16 teeth: global tagged array, int variant store. */ - { "glo_taglo", - "package main;\n" - "let gs: [2](int | bool) = [0, 0];\n" - "export fn main() int = {\n" - " gs[1] = 42;\n" - " match (gs[1]) {\n" - " case let n: int => { if (n != 42) { return 1; }; return 0; };\n" - " case bool => { return 2; };\n" - " };\n" - "};\n", - 0 }, - - /* glo_taghi — bool variant via gs[1]=true; the tag must remap. */ - { "glo_taghi", - "package main;\n" - "let gs: [2](int | bool) = [0, 0];\n" - "export fn main() int = {\n" - " gs[1] = true;\n" - " match (gs[1]) {\n" - " case int => { return 1; };\n" - " case let b: bool => { if (!b) { return 2; }; return 0; };\n" - " };\n" - "};\n", - 0 }, - - /* glo_varidx — runtime index over the global base (scaled IMULQ). */ - { "glo_varidx", - "package main;\n" - "let gs: [3](int | bool) = [0, 0, 0];\n" - "export fn main() int = {\n" - " let i: int = 2;\n" - " gs[i] = 99;\n" - " match (gs[2]) {\n" - " case let n: int => { if (n != 99) { return 1; }; return 0; };\n" - " case bool => { return 2; };\n" - " };\n" - "};\n", - 0 }, - - /* glo_str3w — str payload (3-word header); the multi-word slot copy - * over the global base must be complete. */ - { "glo_str3w", - "package main;\n" - "let gs: [2](int | str) = [0, 0];\n" - "export fn main() int = {\n" - " gs[1] = \"hello\";\n" - " match (gs[1]) {\n" - " case int => { return 1; };\n" - " case let s: str => { if (s.len: int != 5) { return 2; }; return 0; };\n" - " };\n" - "};\n", - 0 }, - - /* glo_struct2w — a 16B struct payload (slot = tag + 2 payload words); - * proves the mid-count copy loop moves BOTH payload words. */ - { "glo_struct2w", - "package main;\n" - "type pair = struct { a: i64, b: i64 };\n" - "let gs: [2](int | pair) = [0, 0];\n" - "export fn main() int = {\n" - " gs[1] = pair { a = 0x1111, b = 0x2222 };\n" - " match (gs[1]) {\n" - " case int => { return 1; };\n" - " case let q: pair => {\n" - " if (q.a != 0x1111) { return 2; };\n" - " if (q.b != 0x2222) { return 3; };\n" - " return 0;\n" - " };\n" - " };\n" - "};\n", - 0 }, - - /* loc_ctl — LOCAL tagged array: the frame-base path that already worked - * must stay correct (the fix must not perturb the non-global branch). */ - { "loc_ctl", - "package main;\n" - "export fn main() int = {\n" - " let a: [2](int | bool) = [0, 0];\n" - " a[1] = 42;\n" - " match (a[1]) {\n" - " case let n: int => { if (n != 42) { return 1; }; return 0; };\n" - " case bool => { return 2; };\n" - " };\n" - "};\n", - 0 }, - - /* scalar_ctl — scalar global array: the template arm the fix mirrors. */ - { "scalar_ctl", - "package main;\n" - "let arr: [3]int = [0, 0, 0];\n" - "export fn main() int = {\n" - " arr[1] = 42;\n" - " if (arr[1] != 42) { return 1; };\n" - " return 0;\n" - "};\n", - 0 }, -}; - -/* 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], cmd[1024], rmcmd[160]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/tagglobidx_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tagglobidx_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tagglobidx_%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, "taggedglobalindex_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, "taggedglobalindex_run[cstage][%s]: exit=%d " - "want=%d (global base dropped / store lost)\n", - rows[i].label, gc, rows[i].want_exit); - fail++; - } - if (!have_ww) { - fprintf(stderr, "taggedglobalindex_run: skip wwstage (no %s)\n", - wdrv); - continue; - } - int gw = run_build(wdrv, &rows[i], i); - if (gw != gc) { - fprintf(stderr, "taggedglobalindex_run[%s]: cs=%d != ww=%d " - "(#16 indexed-global-tagged base divergence)\n", - rows[i].label, gc, gw); - fail++; - } - if (rows[i].want_exit >= 0 && gw != rows[i].want_exit) { - fprintf(stderr, "taggedglobalindex_run[wwstage][%s]: exit=%d " - "want=%d (global base dropped / store lost)\n", - rows[i].label, gw, rows[i].want_exit); - fail++; - } - } - - if (fail) { - fprintf(stderr, "taggedglobalindex_run: %d/%d checks failed\n", - fail, total); - return 1; - } - printf("taggedglobalindex_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/989_taggedidx_run.c b/test/wcc/989_taggedidx_run.c deleted file mode 100644 index 56fe590a..00000000 --- a/test/wcc/989_taggedidx_run.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * 989_taggedidx_run — F7-c6 (#23): a tagged-union element indexed off a - * CALL or SLICE base must load the full tag+payload cursor, both stages. - * - * THE BUG (cat-A silent miscompile, gate-blind): cgindex - * (selfhost/cmd/wcc/cgenexpr.ww) classifies an indexed element as tagged - * so it loads the (tag, val0, val1) slot. The non-ident-base arm gated - * that classify on a base-kind WHITELIST (N_DOT / N_INDEX / N_UN-deref), - * so a tagged element indexed off an N_CALL base (`f()[i]`) or N_SLICE - * base (`s[a:b][i]`) fell through to the scalar load — a lone - * `MOVQ (AX),AX` (tag word only), dropping the payload. cstage classifies - * for ANY base off the stamped element tinfo (`esubu->kind == TY_TAGGED`, - * cmd/w6c/cgen.c:8101), so it loaded the full cursor and ran correct - * (cs rc=50 / ww rc=40 in the review repro — the cat-A divergence). - * 990-997 stay green because the corpus never indexes a tagged element - * off a call/slice base; only a runtime row catches it. THE FIX: drop the - * base-kind whitelist — classify the tagged element off n.type_ for any - * non-N_IDENT base, aligning wwstage UP. - * - * Arrays of tagged are built by per-element store; box = (i64|bool). - * - * Rows (cstage `ww` + gated wwstage `ww_ww`; rule-10 + absolute value): - * row | shape | want - * -----------------+--------------------------------+------ - * slice_tagged_idx | arr[1:][0] (N_SLICE base) | 40 [#23 bug] - * call_tagged_idx | getrows()[1] (N_CALL base) | 50 [#23 bug] - * ident_tagged_idx | arr[1] (N_IDENT base) | 40 (control: the - * | first arm already handled it — no regression) - */ -#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; -}; - -static const struct row rows[] = { - /* (1) #23 — tagged element off a SLICE base `arr[1:][0]`. arr[1] is the - * i64 variant 40; the bug loaded only the tag word → garbage match. */ - { "slice_tagged_idx", - "package main;\n" - "type box = (i64 | bool);\n" - "export fn main() int = {\n" - " let arr: [3]box;\n" - " arr[0] = 10; arr[1] = 40; arr[2] = 70;\n" - " let v: box = arr[1:][0];\n" - " let r: i64 = 0;\n" - " match (v) {\n" - " case let x: i64 => { r = x; };\n" - " case let b: bool => { r = 99; };\n" - " };\n" - " return r: int;\n" - "};\n", - 40 }, - - /* (2) #23 — tagged element off a CALL base `getrows()[1]`. The fn - * returns []box; element 1 is the i64 variant 50. */ - { "call_tagged_idx", - "package main;\n" - "type box = (i64 | bool);\n" - "fn getrows() []box = {\n" - " let arr: [3]box;\n" - " arr[0] = 10; arr[1] = 50; arr[2] = 70;\n" - " return arr[0:];\n" - "};\n" - "export fn main() int = {\n" - " let v: box = getrows()[1];\n" - " let r: i64 = 0;\n" - " match (v) {\n" - " case let x: i64 => { r = x; };\n" - " case let b: bool => { r = 99; };\n" - " };\n" - " return r: int;\n" - "};\n", - 50 }, - - /* (3) control — tagged element off an N_IDENT base `arr[1]` (the first - * cgindex arm already handled it via istaggedtype on the base tnode): - * c6 must not regress it. arr[1] == 40. */ - { "ident_tagged_idx", - "package main;\n" - "type box = (i64 | bool);\n" - "export fn main() int = {\n" - " let arr: [3]box;\n" - " arr[0] = 10; arr[1] = 40; arr[2] = 70;\n" - " let v: box = arr[1];\n" - " let r: i64 = 0;\n" - " match (v) {\n" - " case let x: i64 => { r = x; };\n" - " case let b: bool => { r = 99; };\n" - " };\n" - " return r: int;\n" - "};\n", - 40 }, -}; - -/* 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/tagidx_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tagidx_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tagidx_%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); - - struct { const char *name; const char *drv; 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].drv, X_OK) != 0) { - fprintf(stderr, "taggedidx_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].drv); - continue; - } - for (int i = 0; i < n; i++) { - total++; - int got = run_build(drivers[d].drv, &rows[i], i); - if (got != rows[i].want_exit) { - fprintf(stderr, "taggedidx_run[%s][%s]: exit=%d " - "want=%d\n", drivers[d].name, rows[i].label, - got, rows[i].want_exit); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "taggedidx_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("taggedidx_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/989_tagnorm_run.c b/test/wcc/989_tagnorm_run.c deleted file mode 100644 index 10802e43..00000000 --- a/test/wcc/989_tagnorm_run.c +++ /dev/null @@ -1,265 +0,0 @@ -/* - * 989_tagnorm_run — F1 (#1/#3): tagged-union type-set normalization must be - * symmetric across stages. cstage resolve_type N_TTAGGED (cmd/wcc/check.c: - * 801-882) drops `never`, dedups variants (variant_match: NAMED nominal, - * else structural), collapses a lone survivor to that variant, and folds a - * two-variant `(*T|void)` to an 8B nullable pointer. The wwstage formerly - * skipped ALL four at BOTH size sources — astsize (selfhost/cmd/wcc/check.ww) - * and tinfofornode N_TTAGGED — so size()/align() folded the RAW declaration - * list and tag numbering ran off it too. - * - * THE BUG (cat-A silent miscompile, gate-blind): size((*u8|void)) folded - * 16 in ww vs 8 in cs; size((i32|never)) 16 vs 4; align((i32|never)) 8 vs 4; - * size((i32|i32)) 16 vs 4 — all rc=0 both stages, divergent constant baked - * into the .s (MOVQ $16 vs $8/$4). The corpus never declares a never/dup/ - * collapsible/nullable union whose SIZE it folds, so 990-997 stayed green; - * only a runtime size-fold row catches it. - * - * THE FIX: ONE normalization site — tinfofornode N_TTAGGED (the tinfo SSoT - * cgen already reads via ti.params/ti.size) gained never-drop + dedup + - * single-collapse; astsize/astalign N_TTAGGED delegate to it (mirroring the - * existing N_TTUPLE arm). cgen's deduped tag numbering rides ti.params for - * free. - * - * Rows (cstage `ww` + gated wwstage `ww_ww`; rule-10 + absolute value): - * row | shape | want | pre-fix ww - * ---------------+-------------------------------+------+----------- - * nullable_size | size((*u8|void)) | 8 | 16 [#1] - * never_size | size((i32|never)) | 4 | 16 [#1] - * dup_size | size((i32|i32)) | 4 | 16 [#1] - * never_align | align((i32|never)) | 4 | 8 [#1] - * dedup_match | "hi": (i32|i32|str), match str | 7 | 7 (green - * | both — dedup keeps construct/match consistent; the - * | tag-NUMBER divergence #3 is byte-id, caught by B1) - * void_dedup_ret | bare `return;` of (i32|i32|void)| 5 | !5 [voidvar- - * | then match the void variant | | iantindex fold] - * void_pos0_ret | bare `return;` of (void|i32) | 5 | 5 (idx-0 - * | | | boundary) - * void_pos2_ret | bare `return;` of (i32|str|void)| 5 | !5 [idx-2 - * | | | teeth] - * - * void_{dedup,pos0,pos2}_ret pin voidvariantindex's idx counter at the - * three normalized positions (0/1/2): the first fold omitted `idx += 1`, - * so it returned 0 for ANY void position — only positions 1 and 2 are RED - * under that bug (position 0 is the one value it happened to get right, a - * boundary pin). void_dedup_ret is the gate-blind teeth for the cgenutil - * voidvariantindex - * fold: the bare-`return;` void tag must come off the NORMALIZED (deduped) - * variant chain, like construct/match already do. With the F1 checker dedup - * (i32|i32|void)->(i32|void) but a RAW-list voidvariantindex, bare return - * stores tag 2 while match expects void at tag 1 -> no arm fires, r stays 0 - * (RED). The fold reads ti.params -> tag 1 -> the void arm yields 5. - */ -#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; -}; - -static const struct row rows[] = { - /* (1) #1 — nullable `(*T|void)` folds to an 8B pointer slot, not a - * 16B tag+payload box. */ - { "nullable_size", - "package main;\n" - "type p = (*u8 | void);\n" - "export fn main() int = { return size(p): int; };\n", - 8 }, - - /* (2) #1 — `never` drops, leaving a lone i32 → the union collapses to - * i32 (size 4), not an 8+8 box. */ - { "never_size", - "package main;\n" - "type q = (i32 | never);\n" - "export fn main() int = { return size(q): int; };\n", - 4 }, - - /* (3) #1 — duplicate variants dedup to one i32 → collapse to i32. */ - { "dup_size", - "package main;\n" - "type d = (i32 | i32);\n" - "export fn main() int = { return size(d): int; };\n", - 4 }, - - /* (4) #1 — align() rides the same normalization: the collapsed i32 - * aligns to 4, not the tag word's 8. */ - { "never_align", - "package main;\n" - "type q = (i32 | never);\n" - "export fn main() int = { return align(q): int; };\n", - 4 }, - - /* (5) #3 — a deduped `(i32|i32|str)` still constructs+matches its str - * variant correctly in BOTH stages (the str-arm yields 7). Pins - * cs==ww on the functional outcome; the per-stage tag-NUMBER (str=$1 - * vs $2) divergence is rule-10 byte-id, proven by B1 self-compile. */ - { "dedup_match", - "package main;\n" - "type u = (i32 | i32 | str);\n" - "export fn main() int = {\n" - " let v: u = \"hi\";\n" - " let r: i64 = 0;\n" - " match (v) {\n" - " case let s: str => { r = 7; };\n" - " case let x: i32 => { r = 1; };\n" - " };\n" - " return r: int;\n" - "};\n", - 7 }, - - /* (6) #3/F1-fold — a deduped `(i32|i32|void)` returned via bare - * `return;` must tag the void variant off the NORMALIZED chain (idx 1), - * matching the dedup'd construct/match numbering. A raw-list - * voidvariantindex tags it 2 -> no match arm fires -> r stays 0. The - * void arm yields 5. */ - { "void_dedup_ret", - "package main;\n" - "type u = (i32 | i32 | void);\n" - "fn f() u = { return; };\n" - "export fn main() int = {\n" - " let v: u = f();\n" - " let r: i64 = 0;\n" - " match (v) {\n" - " case void => { r = 5; };\n" - " case let x: i32 => { r = 9; };\n" - " };\n" - " return r: int;\n" - "};\n", - 5 }, - - /* (7) #3/F1-fold — void at NORMALIZED index 0 pins the lower - * boundary of voidvariantindex's idx counter (the only position the - * missing `idx += 1` happened to get right). `(void|i32)` keeps void - * first; bare `return;` tags idx 0, the void arm yields 5. */ - { "void_pos0_ret", - "package main;\n" - "type u = (void | i32);\n" - "fn f() u = { return; };\n" - "export fn main() int = {\n" - " let v: u = f();\n" - " let r: i64 = 0;\n" - " match (v) {\n" - " case void => { r = 5; };\n" - " case let x: i32 => { r = 9; };\n" - " };\n" - " return r: int;\n" - "};\n", - 5 }, - - /* (8) #3/F1-fold — void at NORMALIZED index 2 is the stronger teeth - * for the idx counter: a raw-list / non-incrementing voidvariantindex - * tags it 0 (the i32 arm) instead of 2, so r never reaches 5. - * `(i32|str|void)` has no dedup/drop, so void's normalized index IS - * its declaration index 2. */ - { "void_pos2_ret", - "package main;\n" - "type u = (i32 | str | void);\n" - "fn f() u = { return; };\n" - "export fn main() int = {\n" - " let v: u = f();\n" - " let r: i64 = 0;\n" - " match (v) {\n" - " case void => { r = 5; };\n" - " case let s: str => { r = 8; };\n" - " case let x: i32 => { r = 9; };\n" - " };\n" - " return r: int;\n" - "};\n", - 5 }, -}; - -/* 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 src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024]; - snprintf(tmpdir, sizeof tmpdir, "/tmp/tagnorm_%d_d_%d", getpid(), i); - mkdir(tmpdir, 0755); - snprintf(src, sizeof src, "%s/tagnorm_%d_%d.ww", tmpdir, getpid(), i); - snprintf(outbin, sizeof outbin, "%s/tagnorm_%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); - - struct { const char *name; const char *drv; 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].drv, X_OK) != 0) { - fprintf(stderr, "tagnorm_run: skip %s (no %s)\n", - drivers[d].name, drivers[d].drv); - continue; - } - for (int i = 0; i < n; i++) { - total++; - int got = run_build(drivers[d].drv, &rows[i], i); - if (got != rows[i].want_exit) { - fprintf(stderr, "tagnorm_run[%s][%s]: exit=%d " - "want=%d\n", drivers[d].name, rows[i].label, - got, rows[i].want_exit); - fail++; - } - } - } - - if (fail) { - fprintf(stderr, "tagnorm_run: %d/%d fixtures failed\n", - fail, total); - return 1; - } - printf("tagnorm_run: %d/%d ok\n", total, total); - return 0; -} diff --git a/test/wcc/data/idxtagfield_big_assign/case.ww b/test/wcc/data/idxtagfield_big_assign/case.ww new file mode 100644 index 00000000..7b0c833a --- /dev/null +++ b/test/wcc/data/idxtagfield_big_assign/case.ww @@ -0,0 +1,14 @@ +//ww:error "#58: >32B tagged-field indexed store unreachable until #114" +// 944_idx_tagged_field big_assign tripwire: a >32B box STORE via xs[i].m +// LOUD-STOPS in BOTH stages until #114. A SUCCESS = loud-stop regressed to silent. +package main; +type big = (void | size | [5]size); +type row = struct { id: int, m: big }; +export fn main() i32 = { + let xs: [2]row = [ + row { id = 1, m = 7: size }, + row { id = 2, m = 8: size }, + ]; + xs[0].m = 9: size; + return 0; +}; diff --git a/test/wcc/data/idxtagfield_big_read/case.ww b/test/wcc/data/idxtagfield_big_read/case.ww new file mode 100644 index 00000000..4e9123cb --- /dev/null +++ b/test/wcc/data/idxtagfield_big_read/case.ww @@ -0,0 +1,16 @@ +//ww:error "#58: >32B tagged-field indexed read unreachable until #114" +// 944_idx_tagged_field big_read tripwire: a >32B box read via the indexed spine +// (xs[i].m) LOUD-STOPS in BOTH stages (rule 7) until #114 wires >32B box memcpy. +// Reachable via a NARROW init; a SUCCESS here = the loud-stop regressed to silent. +package main; +type big = (void | size | [5]size); +type row = struct { id: int, m: big }; +export fn main() i32 = { + let xs: [2]row = [ + row { id = 1, m = 7: size }, + row { id = 2, m = 8: size }, + ]; + let v: size = xs[0].m as size; + if (v != 7) { return 1; }; + return 0; +}; diff --git a/test/wcc/data/idxtagfield_float_assign/case.ww b/test/wcc/data/idxtagfield_float_assign/case.ww new file mode 100644 index 00000000..5f55397f --- /dev/null +++ b/test/wcc/data/idxtagfield_float_assign/case.ww @@ -0,0 +1,14 @@ +//ww:error "#58: float-payload tagged-field indexed store unreachable until #114" +// 944_idx_tagged_field float_assign tripwire: a float-payload box STORE via +// xs[i].m LOUD-STOPS in BOTH stages until #114. SUCCESS = loud-stop regressed. +package main; +type fo = (void | f64); +type row = struct { id: int, m: fo }; +export fn main() i32 = { + let xs: [2]row = [ + row { id = 1, m = 1.0: f64 }, + row { id = 2, m = 2.0: f64 }, + ]; + xs[0].m = 3.0: f64; + return 0; +}; diff --git a/test/wcc/data/idxtagfield_midword_assign/case.ww b/test/wcc/data/idxtagfield_midword_assign/case.ww new file mode 100644 index 00000000..46cd4035 --- /dev/null +++ b/test/wcc/data/idxtagfield_midword_assign/case.ww @@ -0,0 +1,14 @@ +//ww:error "#58: multi-word tagged-field indexed store unreachable until #114" +// 944_idx_tagged_field midword_assign tripwire: a multi-word (>16B, <=32B box) +// STORE via xs[i].m LOUD-STOPS in BOTH stages until #114. SUCCESS = regressed. +package main; +type mid = (void | size | [3]size); +type row = struct { id: int, m: mid }; +export fn main() i32 = { + let xs: [2]row = [ + row { id = 1, m = 7: size }, + row { id = 2, m = 8: size }, + ]; + xs[0].m = 9: size; + return 0; +}; diff --git a/test/wcc/data/nestunion_collision/case.ww b/test/wcc/data/nestunion_collision/case.ww new file mode 100644 index 00000000..7fbdc3e7 --- /dev/null +++ b/test/wcc/data/nestunion_collision/case.ww @@ -0,0 +1,16 @@ +//ww:error "structural fallback cannot disambiguate nominally-distinct same-shape variants" +// 925_nested_union_widen collision_guard: two distinct names a,b both !(i32|bool) +// in one union (a | b) — without nominal layout the widen cannot pick a variant. +// BOTH stages must loud-stop (runww ERROR arm runs w6c AND w6c_ww). Migrated from +// 989-era 925_nested_union_widen_run.c (#5-C5). +package main; +type a = !(i32 | bool); +type b = !(i32 | bool); +export fn main() i32 = { + let x: a = (1i32: a); + let r: (a | b) = x; + match (r) { + case let p: a => return 1; + case let q: b => return 2; + }; +}; diff --git a/test/wcc/data/vchain_amb_loud/case.ww b/test/wcc/data/vchain_amb_loud/case.ww new file mode 100644 index 00000000..dd795655 --- /dev/null +++ b/test/wcc/data/vchain_amb_loud/case.ww @@ -0,0 +1,15 @@ +//ww:error "source alias chain reaches >=2 variants" +// 944_variant_chain_b95 chain_amb_loud: source base reaches both ali and ali2 +// (chain) — ambiguous without nominal layout (#95). BOTH stages loud-stop. +package main; +type base = struct { a: int, b: int }; +type ali = base; +type ali2 = ali; +export fn main() i32 = { + let s: base; + s.a = 4; s.b = 9; + let v: (ali | ali2) = s; + if (v is ali) { return 1; }; + if (v is ali2) { return 2; }; + return 3; +}; diff --git a/test/wcc/data/vchain_bare_ambig/case.ww b/test/wcc/data/vchain_bare_ambig/case.ww new file mode 100644 index 00000000..f326755e --- /dev/null +++ b/test/wcc/data/vchain_bare_ambig/case.ww @@ -0,0 +1,11 @@ +//ww:error "bare source structurally matches >=2 NAMED variants" +// 944_variant_chain_b95 bare_ambig: a bare *i32 source matches both p1 and p2 +// (#15/#218). BOTH stages loud-stop. +package main; +type p1 = *i32; +type p2 = *i32; +export fn main() i32 = { + let x: i32 = 7; + let v: (p1 | p2) = &x; + return 0; +}; diff --git a/test/wcc/data/vchain_bare_ambig2/case.ww b/test/wcc/data/vchain_bare_ambig2/case.ww new file mode 100644 index 00000000..71265d39 --- /dev/null +++ b/test/wcc/data/vchain_bare_ambig2/case.ww @@ -0,0 +1,13 @@ +//ww:error "bare source structurally matches >=2 NAMED variants" +// 944_variant_chain_b95 bare_ambig2: bare *i32 reaches q1,q2 via b1/b2 chains +// (#15/#218). BOTH stages loud-stop. +package main; +type b1 = *i32; +type b2 = *i32; +type q1 = b1; +type q2 = b2; +export fn main() i32 = { + let x: i32 = 7; + let v: (q1 | q2) = &x; + return 0; +}; diff --git a/test/wcc/data/vchain_isas_amb/case.ww b/test/wcc/data/vchain_isas_amb/case.ww new file mode 100644 index 00000000..9c749aae --- /dev/null +++ b/test/wcc/data/vchain_isas_amb/case.ww @@ -0,0 +1,13 @@ +//ww:error "not a variant" +// 944_variant_chain_b95 isas_amb_reject_notcrash: `is base` on (ali | ali2) +// (both reach base) — must REJECT (not crash) on the ambiguity (#107). BOTH +// stages reject with the diagnostic body, not a signal. +package main; +type base = struct { a: int, b: int }; +type ali = base; +type ali2 = ali; +fn f(v: (ali | ali2)) i32 = { + if (v is base) { return 1; }; + return 0; +}; +export fn main() i32 = { return 0; }; diff --git a/test/wcc/data/vchain_isas_chain/case.ww b/test/wcc/data/vchain_isas_chain/case.ww new file mode 100644 index 00000000..28bb4e50 --- /dev/null +++ b/test/wcc/data/vchain_isas_chain/case.ww @@ -0,0 +1,12 @@ +//ww:error "not a variant" +// 944_variant_chain_b95 isas_chain_reject: `is base` on (void | ali) where +// ali=base — is/as ACCEPTANCE stays nominal-exact (#107). BOTH stages reject +// (cstage "base is not a variant of (...)", wwstage "is/as: not a variant of..."). +package main; +type base = struct { a: int, b: int }; +type ali = base; +fn f(v: (void | ali)) i32 = { + if (v is base) { return 1; }; + return 0; +}; +export fn main() i32 = { return 0; }; diff --git a/test/wcc/data/vchain_isas_unrel/case.ww b/test/wcc/data/vchain_isas_unrel/case.ww new file mode 100644 index 00000000..6f9d396e --- /dev/null +++ b/test/wcc/data/vchain_isas_unrel/case.ww @@ -0,0 +1,12 @@ +//ww:error "not a variant" +// 944_variant_chain_b95 isas_unrel_reject: `is ta` on (void | tb) where ta,tb +// are same-shape but distinct names — is/as acceptance is nominal-exact (#107). +// BOTH stages reject. +package main; +type ta = struct { a: int, b: int }; +type tb = struct { a: int, b: int }; +fn f(v: (void | tb)) i32 = { + if (v is ta) { return 1; }; + return 0; +}; +export fn main() i32 = { return 0; }; diff --git a/test/wcc/data/vchain_twin_prim_amb/case.ww b/test/wcc/data/vchain_twin_prim_amb/case.ww new file mode 100644 index 00000000..809dbd98 --- /dev/null +++ b/test/wcc/data/vchain_twin_prim_amb/case.ww @@ -0,0 +1,12 @@ +//ww:error "source alias chain reaches >=2 variants" +// 944_variant_chain_b95 twin_prim_alias_amb: union (int | ai) ai=int, source aj=int +// — both the bare int variant and ai share the int bottom, ambiguous (#95 +// deviation-2). BOTH stages loud-stop. +package main; +type ai = int; +type aj = int; +export fn main() i32 = { + let s: aj = 7; + let v: (int | ai) = s; + return 0; +};