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