test: migrate Fam9 match/tagged value tests to @test (#5-C5)
fold-2 chunk C5 (drew's Fam8-13 plan), the highest-risk chunk: 21 match/tagged value-row C drivers re-homed. 20 -> test/lang/*_test.ww @test row-tables + 12 runww //ww:error carriers (both stages reject). The global-tag cluster (globtag*/globstructwiden/taggedderefstore/...), which sits on the #15/#17 global-ptr fix, was empirically probed byte-id CLEAN -- the predicted hotspot surfaced ZERO fresh cs!=ww. Carves: variant_chain_b95 #81 -> _runonly (genuinely diverges at HEAD); callret_bound277 #277 -> slim C pin (cs-runs/ww-rejects), mutation-gated. 929_tagged_memarg kept whole (SSE-ABI asm conformance). 19 drivers deleted, 944_variant_chain slimmed to the #277 pin. The match-on-tagged-struct-field divergence (former #26) probed RESOLVED for all its cited shapes (938 voidstr_field/recursion_torture, tagnorm dedup_match all byte-id cs==ww + value-correct) -- closed no-reproducer-at-HEAD, attribution to #15/#17 INFERRED. Those rows migrate as normal byte-id @test and serve as the REGRESSION SENTINEL for the inferred close (a resurgence trips the gate). LANGBYTEID floor 93->113; test count 374->355 (19 deleted; 929 + 944_variant_chain kept). do-not-auto-batch (926_tagscr/940_global_sret/940_str_forrange) untouched.
This commit is contained in:
184
Makefile
184
Makefile
@@ -288,13 +288,7 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_zerotest_run \
|
||||
$(BIN)/test_ampfncollide_run \
|
||||
$(BIN)/test_trycallcollide_run \
|
||||
$(BIN)/test_taggedidx_run \
|
||||
$(BIN)/test_fnptrcollide_run \
|
||||
$(BIN)/test_tagnorm_run \
|
||||
$(BIN)/test_globtagisas_run \
|
||||
$(BIN)/test_globtagreassign_run \
|
||||
$(BIN)/test_globstructwiden_run \
|
||||
$(BIN)/test_globtagwiden_run \
|
||||
$(BIN)/test_arr_ptr_global \
|
||||
$(BIN)/test_def_arr_infer_len \
|
||||
$(BIN)/test_def_arr_len \
|
||||
@@ -353,26 +347,17 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_signed_data_emit \
|
||||
$(BIN)/test_signed_data_emit_run \
|
||||
$(BIN)/test_tagged_call_arg \
|
||||
$(BIN)/test_tagged_call_arg_run \
|
||||
$(BIN)/test_tryprop_tag_remap_run \
|
||||
$(BIN)/test_nullable_try_run \
|
||||
$(BIN)/test_nullable_assert_run \
|
||||
$(BIN)/test_nested_union_widen_run \
|
||||
$(BIN)/test_sret_struct_return \
|
||||
$(BIN)/test_tagged_sret_run \
|
||||
$(BIN)/test_void_error_singleton_run \
|
||||
$(BIN)/test_tagged_memarg_run \
|
||||
$(BIN)/test_tagscr_sizes_run \
|
||||
$(BIN)/test_is_nonident_run \
|
||||
$(BIN)/test_match_nonident_idx_run \
|
||||
$(BIN)/test_global_sret_run \
|
||||
$(BIN)/test_sret_narrow_field \
|
||||
$(BIN)/test_match_slice_variant \
|
||||
$(BIN)/test_match_slice_variant_run \
|
||||
$(BIN)/test_array_zeroinit_run \
|
||||
$(BIN)/test_alias_accept_run \
|
||||
$(BIN)/test_idx_tagged_field_run \
|
||||
$(BIN)/test_tagged_widen_arg_run \
|
||||
$(BIN)/test_alias_cgen_b5_run \
|
||||
$(BIN)/test_alias_cgen_b6_run \
|
||||
$(BIN)/test_alias_emit_b7_run \
|
||||
@@ -381,7 +366,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_const_slice_aggregate_run \
|
||||
$(BIN)/test_append_structlit_evalorder_run \
|
||||
$(BIN)/test_xmod_fnptr_const_run \
|
||||
$(BIN)/test_tagged_structlit_payload_run \
|
||||
$(BIN)/test_forrange_fieldbase_run \
|
||||
$(BIN)/test_errtype_compare \
|
||||
$(BIN)/test_intbinop_mismatch \
|
||||
@@ -394,7 +378,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_match_4arm_cross_module \
|
||||
$(BIN)/test_match_4arm_cross_module_run \
|
||||
$(BIN)/test_free_noop_run \
|
||||
$(BIN)/test_variant_typekey_run \
|
||||
$(BIN)/test_enum_modshadow \
|
||||
$(BIN)/test_struct_modshadow \
|
||||
$(BIN)/test_def_modshadow \
|
||||
@@ -479,8 +462,6 @@ TESTS = $(BIN)/test_smoke $(BIN)/test_lex $(BIN)/test_parse $(BIN)/test_check \
|
||||
$(BIN)/test_single_field_struct_zeroinit \
|
||||
$(BIN)/test_structvariant_largeunion_return \
|
||||
$(BIN)/test_narrow_alias_deref_store \
|
||||
$(BIN)/test_taggedderefstore_run \
|
||||
$(BIN)/test_taggedglobalindex_run \
|
||||
$(BIN)/test_bufio_vstream_run \
|
||||
$(BIN)/test_log_vstream_run \
|
||||
$(BIN)/test_use_promote_alias \
|
||||
@@ -833,40 +814,6 @@ $(BIN)/test_structlocal_frame: test/wcc/989_structlocal_frame.c \
|
||||
$(BIN)/w6c $(BIN)/w6c_ww | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_taggedderefstore_run (#17): a deref-target store `*p = v` (p:*tagged)
|
||||
# sized the write off the pointee and emitted ONE fldstoreop — rhs landed in
|
||||
# the tag word, payload dropped, the union discriminant corrupted. BOTH-stage-
|
||||
# wrong and byte-id-green (#263-class, gate-blind): cstage cmd/w6c/cgen.c #17
|
||||
# deref else-branch + wwstage cgenexpr.ww #17 single-store tail emitted the
|
||||
# SAME wrong asm. #17 routes the tagged-pointee deref store through the widener
|
||||
# (cg_widen_tagged_store / cgwidentaggedstore) via the shared tag scratch, then
|
||||
# word-copies scratch -> *p (mirror of the index-element tagged arm). Builds+
|
||||
# runs on BOTH driver twins (rule-10), pinning the absolute 0; scalarctl pins
|
||||
# the unchanged non-tagged single-store path.
|
||||
$(BIN)/test_taggedderefstore_run: test/wcc/989_taggedderefstore_run.c \
|
||||
$(BIN)/ww $(BIN)/ww_ww \
|
||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_taggedglobalindex_run (#16): an indexed store into a GLOBAL tagged array
|
||||
# `gs[i] = v` (gs:[N](A|B)) — the indexed-tagged-element assign arm (cmd/w6c/
|
||||
# cgen.c ~6502) lacked the `isglobal -> LEAQ name(SB)` base branch the scalar
|
||||
# `arr[i]=v` element arm (~6767) already had, so the global base resolved to a
|
||||
# junk BP offset and the widened store landed nowhere; cstage silently returned
|
||||
# 0 while wwstage (cgenexpr.ww index arm, already global-aware) returned the
|
||||
# correct value — a cs!=ww divergence the byte-id gate could SEE. #16 aligns
|
||||
# cstage UP with the mirrored isglobal branch. Builds+runs on BOTH driver twins
|
||||
# (rule-10), pinning the absolute 0; loc_ctl + scalar_ctl pin the unchanged
|
||||
# frame-base and scalar-template paths.
|
||||
$(BIN)/test_taggedglobalindex_run: test/wcc/989_taggedglobalindex_run.c \
|
||||
$(BIN)/ww $(BIN)/ww_ww \
|
||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_arrlit_tail_zero_run (#13): an under-length array literal zero-fills the
|
||||
# unspecified tail, not the last value. Builds+runs on BOTH driver twins
|
||||
# (rule-10), pinning the absolute value (pre-fix ww tail = last value).
|
||||
@@ -990,26 +937,6 @@ $(BIN)/test_trycallcollide_run: test/wcc/989_trycallcollide_run.c \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_taggedidx_run (F7-c6, #23): a tagged-union element indexed off a call
|
||||
# or slice base must load the full tag+payload cursor. Builds+runs on BOTH
|
||||
# driver twins (rule-10).
|
||||
$(BIN)/test_taggedidx_run: test/wcc/989_taggedidx_run.c \
|
||||
$(BIN)/ww $(BIN)/ww_ww \
|
||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_tagnorm_run (F1, #1/#3): tagged-union type-set normalization (never-drop,
|
||||
# dedup, single-collapse, nullable fold) must be symmetric — size()/align()
|
||||
# fold + tag numbering. wwstage gated. See the test header.
|
||||
$(BIN)/test_tagnorm_run: test/wcc/989_tagnorm_run.c \
|
||||
$(BIN)/ww $(BIN)/ww_ww \
|
||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_fnptrcollide_run (F7-c7, #14): a value ident whose leaf collides with
|
||||
# a fn name must not be mis-folded into the fn's TEXT reloc. Build-must-fail
|
||||
# on BOTH driver twins (rule-10); wwstage gated. See the test header.
|
||||
@@ -1020,49 +947,6 @@ $(BIN)/test_fnptrcollide_run: test/wcc/989_fnptrcollide_run.c \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_globtagisas_run (F8-c3, #18): `is`/`as` on a module-global tagged
|
||||
# ident must read tag/payload from g(SB), not saved-BP. MIXED per-half:
|
||||
# `is` align-UP (cs==ww); `as` #263 ww-runtime-correct (cs!=ww residual,
|
||||
# cstage half = task #46). Per-driver expected rows. See the test header.
|
||||
$(BIN)/test_globtagisas_run: test/wcc/989_globtagisas_run.c \
|
||||
$(BIN)/ww $(BIN)/ww_ww \
|
||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_globtagreassign_run (F8-c5, #32, #263): reassigning a module-global
|
||||
# tagged ident (g = expr) must store tag+payload. ww-runtime-correct; cstage
|
||||
# drops the store (cs!=ww residual, cstage half = task #41). Per-driver
|
||||
# expected rows. See the test header.
|
||||
$(BIN)/test_globtagreassign_run: test/wcc/989_globtagreassign_run.c \
|
||||
$(BIN)/ww $(BIN)/ww_ww \
|
||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_globstructwiden_run (F8-c7, #50, #263): widening a module-global struct
|
||||
# ident into a tagged union must copy the full payload. ww-runtime-correct;
|
||||
# cstage zero-fills the payload (cs!=ww residual, cstage half = task #43).
|
||||
$(BIN)/test_globstructwiden_run: test/wcc/989_globstructwiden_run.c \
|
||||
$(BIN)/ww $(BIN)/ww_ww \
|
||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
# 989_globtagwiden_run (F8-c8, #51, #263): widening a module-global tagged
|
||||
# ident into a wider union must copy the whole box as the nested payload.
|
||||
# ww-runtime-correct; cstage copies frame garbage — non-deterministic, so the
|
||||
# cstage rows assert only divergence (cs!=ww, build-ok), cstage half = task #44.
|
||||
$(BIN)/test_globtagwiden_run: test/wcc/989_globtagwiden_run.c \
|
||||
$(BIN)/ww $(BIN)/ww_ww \
|
||||
$(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_let_global: test/wcc/630_let_global.c $(BIN)/ww $(BIN)/w6c \
|
||||
$(BIN)/w6a $(BIN)/w6l $(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
@@ -2068,24 +1952,6 @@ $(BIN)/test_tagged_call_arg: test/wcc/720_tagged_call_arg.c \
|
||||
$(BIN)/w6c $(BIN)/w6c_ww | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_tagged_call_arg_run: test/wcc/924_tagged_call_arg_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_tryprop_tag_remap_run: test/wcc/925_tryprop_tag_remap_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_nested_union_widen_run: test/wcc/925_nested_union_widen_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_nullable_try_run: test/wcc/949_nullable_try_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
@@ -2108,12 +1974,6 @@ $(BIN)/test_tagged_sret_run: test/wcc/926_tagged_sret_run.c \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_void_error_singleton_run: test/wcc/949_void_error_singleton_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_tagged_memarg_run: test/wcc/929_tagged_memarg_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
@@ -2126,18 +1986,6 @@ $(BIN)/test_tagscr_sizes_run: test/wcc/926_tagscr_sizes_run.c \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_is_nonident_run: test/wcc/927_is_nonident_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_match_nonident_idx_run: test/wcc/928_match_nonident_idx_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_global_sret_run: test/wcc/940_global_sret_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
@@ -2152,12 +2000,6 @@ $(BIN)/test_match_slice_variant: test/wcc/722_match_slice_variant.c \
|
||||
$(BIN)/w6c $(BIN)/w6c_ww | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_match_slice_variant_run: test/wcc/926_match_slice_variant_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_str_forrange_loopvar_run: test/wcc/940_str_forrange_loopvar_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
@@ -2176,18 +2018,6 @@ $(BIN)/test_alias_accept_run: test/wcc/944_alias_accept_run.c \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_idx_tagged_field_run: test/wcc/944_idx_tagged_field_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_tagged_widen_arg_run: test/wcc/944_tagged_widen_arg_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_alias_cgen_b5_run: test/wcc/944_alias_cgen_b5_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
@@ -2233,12 +2063,6 @@ $(BIN)/test_xmod_fnptr_const_run: test/wcc/949_xmod_fnptr_const_run.c \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_tagged_structlit_payload_run: test/wcc/938_tagged_structlit_payload_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_forrange_fieldbase_run: test/wcc/937_forrange_fieldbase_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
@@ -2430,12 +2254,6 @@ $(BIN)/test_free_noop_run: test/wcc/930_free_noop_run.c \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_variant_typekey_run: test/wcc/931_variant_typekey_run.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(BIN)/ww_ww $(BIN)/w6c_ww $(BIN)/w6a_ww $(BIN)/w6l_ww \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
$(CC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(BIN)/test_use_promote_alias: test/wcc/699_use_promote_alias.c \
|
||||
$(BIN)/ww $(BIN)/w6c $(BIN)/w6a $(BIN)/w6l \
|
||||
$(LIB)/libwwrt.a | $(BIN)
|
||||
@@ -3098,7 +2916,7 @@ test-lang: all
|
||||
LANGBYTEID_DIR = $(OUT)/langbyteid
|
||||
LANGBYTEID_FILES = $(filter-out %_runonly_test.ww,$(wildcard test/lang/*_test.ww))
|
||||
LANGBYTEID_VERB = test -c
|
||||
LANGBYTEID_EXPECTED_MIN = 93
|
||||
LANGBYTEID_EXPECTED_MIN = 113
|
||||
$(if $(LANGBYTEID_FILES),,$(error test-lang-byteid: empty corpus))
|
||||
test-lang-byteid: all
|
||||
@set -e; \
|
||||
|
||||
29
test/lang/globstructwiden_test.ww
Normal file
29
test/lang/globstructwiden_test.ww
Normal file
@@ -0,0 +1,29 @@
|
||||
// globstructwiden_test — F8-c7 (#50, #263): widening a module-GLOBAL struct
|
||||
// ident into a tagged union must copy the full payload into the box, not just
|
||||
// word0. Was a cat-A both-stage-wrong, now CLOSED both stages (.s byte-id).
|
||||
// Migrated from test/wcc/989_globstructwiden_run.c (K_RUN value rows). The
|
||||
// global lives at module scope so the widen exercises the g(SB) source arm.
|
||||
|
||||
package globstructwiden_test;
|
||||
|
||||
type pt = struct { a: i64, b: i64 };
|
||||
type tri = struct { a: i64, b: i64, c: i64 };
|
||||
|
||||
let gp16: pt = pt { a = 5, b = 6 };
|
||||
let gp24: tri = tri { a = 5, b = 6, c = 7 };
|
||||
|
||||
@test fn widen_16() void = {
|
||||
let x: (pt | str) = gp16;
|
||||
match (x) {
|
||||
case let q: pt => { assert(q.a == 5 && q.b == 6); };
|
||||
case str => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn widen_24() void = {
|
||||
let x: (tri | str) = gp24;
|
||||
match (x) {
|
||||
case let q: tri => { assert(q.a == 5 && q.b == 6 && q.c == 7); };
|
||||
case str => { assert(false); };
|
||||
};
|
||||
};
|
||||
30
test/lang/globtagisas_test.ww
Normal file
30
test/lang/globtagisas_test.ww
Normal file
@@ -0,0 +1,30 @@
|
||||
// globtagisas_test — F8-c3 (#18) + #46: `is`/`as` on a module-GLOBAL tagged-
|
||||
// union ident must read tag/payload from g(SB), not the saved-BP word. The `is`
|
||||
// half was align-UP (ww read 0(BP)); the `as` half was #263 both-wrong (cstage
|
||||
// spilled uninitialized DX). Both CLOSED, .s byte-id. Migrated from
|
||||
// test/wcc/989_globtagisas_run.c (K_RUN value rows). Globals kept at module
|
||||
// scope so the read fires the g(SB) path.
|
||||
|
||||
package globtagisas_test;
|
||||
|
||||
let g_is: (i64 | bool) = 5;
|
||||
let g_asi: (i64 | bool) = 42;
|
||||
let g_ass: (str | i64) = "hello";
|
||||
|
||||
@test fn is_match() void = {
|
||||
assert(g_is is i64);
|
||||
};
|
||||
|
||||
@test fn is_nomatch() void = {
|
||||
assert(!(g_is is bool));
|
||||
};
|
||||
|
||||
@test fn as_i64() void = {
|
||||
let x: i64 = g_asi as i64;
|
||||
assert(x == 42);
|
||||
};
|
||||
|
||||
@test fn as_str() void = {
|
||||
let s: str = g_ass as str;
|
||||
assert(len(s) == 5);
|
||||
};
|
||||
34
test/lang/globtagreassign_test.ww
Normal file
34
test/lang/globtagreassign_test.ww
Normal file
@@ -0,0 +1,34 @@
|
||||
// globtagreassign_test — reassigning a module-GLOBAL tagged-union stores the
|
||||
// full box at g(SB), not just the tag word. Migrated from
|
||||
// test/wcc/989_globtagreassign_run.c (K_RUN value rows, cs==ww). Globals kept
|
||||
// at module scope; each @test reassigns then matches.
|
||||
|
||||
package globtagreassign_test;
|
||||
|
||||
let g_i64: (i64 | void) = 0;
|
||||
let g_str: (str | i64) = 0;
|
||||
let g_union: (i64 | bool) = 0;
|
||||
|
||||
@test fn reassign_i64() void = {
|
||||
g_i64 = 7;
|
||||
match (g_i64) {
|
||||
case let n: i64 => { assert(n == 7); };
|
||||
case void => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn reassign_str() void = {
|
||||
g_str = "hello";
|
||||
match (g_str) {
|
||||
case let s: str => { assert(len(s) == 5); };
|
||||
case i64 => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn reassign_union() void = {
|
||||
g_union = 5;
|
||||
match (g_union) {
|
||||
case let n: i64 => { assert(n == 5); };
|
||||
case bool => { assert(false); };
|
||||
};
|
||||
};
|
||||
37
test/lang/globtagwiden_test.ww
Normal file
37
test/lang/globtagwiden_test.ww
Normal file
@@ -0,0 +1,37 @@
|
||||
// globtagwiden_test — F8-c8 (#51, #263): widening a module-GLOBAL tagged-union
|
||||
// ident into a wider union must copy gi's whole box as the nested payload, not
|
||||
// store its tag as the payload. CLOSED both stages, .s byte-id. Migrated from
|
||||
// test/wcc/989_globtagwiden_run.c (value rows). Globals kept at module scope.
|
||||
|
||||
package globtagwiden_test;
|
||||
|
||||
type inner = (i64 | bool);
|
||||
|
||||
let gi47: inner = 47;
|
||||
let gi99: inner = 99;
|
||||
|
||||
@test fn nest_47() void = {
|
||||
let x: (inner | str) = gi47;
|
||||
match (x) {
|
||||
case let n: inner => {
|
||||
match (n) {
|
||||
case let v: i64 => { assert(v == 47); };
|
||||
case bool => { assert(false); };
|
||||
};
|
||||
};
|
||||
case str => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn nest_99() void = {
|
||||
let x: (inner | str) = gi99;
|
||||
match (x) {
|
||||
case let n: inner => {
|
||||
match (n) {
|
||||
case let v: i64 => { assert(v == 99); };
|
||||
case bool => { assert(false); };
|
||||
};
|
||||
};
|
||||
case str => { assert(false); };
|
||||
};
|
||||
};
|
||||
52
test/lang/idx_tagged_field_test.ww
Normal file
52
test/lang/idx_tagged_field_test.ww
Normal file
@@ -0,0 +1,52 @@
|
||||
// idx_tagged_field_test — xs[i].f tagged-field read (const/var index, 2-tag
|
||||
// union) and assign-back through a pointer. Migrated from
|
||||
// test/wcc/944_idx_tagged_field_run.c (value rows; the C driver asserted cs==ww
|
||||
// .s). The >32B / multi-word / float-payload arms LOUD-STOP (#58) and live as
|
||||
// runww //ww:error carriers (test/wcc/data/idxtagfield_*); #114 graduates them.
|
||||
|
||||
package idx_tagged_field_test;
|
||||
|
||||
type opt = (void | size);
|
||||
type opt3 = (void | bool | size);
|
||||
type row = struct { id: int, m: opt };
|
||||
type row3 = struct { id: int, m: opt3 };
|
||||
|
||||
@test fn const_read() void = {
|
||||
let xs: [2]row = [
|
||||
row { id = 1, m = 7: size },
|
||||
row { id = 2, m = 8: size },
|
||||
];
|
||||
let v: size = xs[0].m as size;
|
||||
assert(v == 7);
|
||||
};
|
||||
|
||||
@test fn var_read() void = {
|
||||
let xs: [2]row = [
|
||||
row { id = 1, m = 7: size },
|
||||
row { id = 2, m = 8: size },
|
||||
];
|
||||
let i: size = 1;
|
||||
let v: size = xs[i].m as size;
|
||||
assert(v == 8);
|
||||
};
|
||||
|
||||
@test fn tag2_read() void = {
|
||||
let xs: [2]row3 = [
|
||||
row3 { id = 1, m = 5: size },
|
||||
row3 { id = 2, m = 9: size },
|
||||
];
|
||||
let i: size = 1;
|
||||
let v: size = xs[i].m as size;
|
||||
assert(v == 9);
|
||||
};
|
||||
|
||||
@test fn assign_back() void = {
|
||||
let xs: [2]row = [
|
||||
row { id = 1, m = 99: size },
|
||||
row { id = 2, m = 2: size },
|
||||
];
|
||||
xs[0].m = 8: size;
|
||||
let p: *row = &xs[0];
|
||||
let v: size = p.m as size;
|
||||
assert(v == 8);
|
||||
};
|
||||
90
test/lang/is_nonident_test.ww
Normal file
90
test/lang/is_nonident_test.ww
Normal file
@@ -0,0 +1,90 @@
|
||||
// is_nonident_test — `e is T` on non-ident scrutinees (indexed, dot-field,
|
||||
// call-result, slice-variant, nullable (*T|void) on dot-field and ident). Each
|
||||
// pins both polarities. Migrated from test/wcc/927_is_nonident_run.c (value
|
||||
// rows; the C driver already asserted cs==ww .s, so byte-id holds).
|
||||
|
||||
package is_nonident_test;
|
||||
|
||||
type p48 = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64 };
|
||||
type small = (p48 | bool);
|
||||
type holder = struct { v: small, k: i64 };
|
||||
|
||||
type val = (i64 | bool);
|
||||
type valslice = (i64 | []u8);
|
||||
|
||||
type t = struct { a: i64 };
|
||||
type maybe = (*t | void);
|
||||
type nholder = struct { m: maybe, k: i64 };
|
||||
|
||||
fn mk(flag: bool) val = {
|
||||
if (flag) {
|
||||
let b: bool = true;
|
||||
return b;
|
||||
};
|
||||
let n: i64 = 7;
|
||||
return n;
|
||||
};
|
||||
|
||||
@test fn indexed() void = {
|
||||
let xs: []small;
|
||||
let b: bool = true;
|
||||
let v: small = b;
|
||||
append(xs, v);
|
||||
assert(xs.len != 0 && xs[xs.len - 1] is bool);
|
||||
assert(!(xs[xs.len - 1] is p48));
|
||||
assert(xs[0] is bool);
|
||||
};
|
||||
|
||||
@test fn dot_field() void = {
|
||||
let b: bool = true;
|
||||
let h: holder = holder { v = b, k = 5 };
|
||||
assert(h.v is bool);
|
||||
assert(!(h.v is p48));
|
||||
};
|
||||
|
||||
@test fn call_result() void = {
|
||||
assert(mk(true) is bool);
|
||||
assert(mk(false) is i64);
|
||||
assert(!(mk(true) is i64));
|
||||
};
|
||||
|
||||
@test fn slice_variant_indexed() void = {
|
||||
let xs: []valslice;
|
||||
let buf: [2]u8 = [1u8, 2u8];
|
||||
let s: []u8 = buf[0:2];
|
||||
let v: valslice = s;
|
||||
append(xs, v);
|
||||
let n: i64 = 5;
|
||||
let w: valslice = n;
|
||||
append(xs, w);
|
||||
assert(xs[0] is []u8);
|
||||
assert(xs[1] is i64);
|
||||
assert(!(xs[0] is i64));
|
||||
};
|
||||
|
||||
@test fn nullable_dot_field() void = {
|
||||
let v: t = t { a = 5 };
|
||||
let h: nholder = nholder { m = &v, k = 1 };
|
||||
assert(h.m is *t);
|
||||
assert(!(h.m is void));
|
||||
let h2: nholder = nholder { m = void, k = 2 };
|
||||
assert(!(h2.m is *t));
|
||||
assert(h2.m is void);
|
||||
};
|
||||
|
||||
@test fn nullable_ident() void = {
|
||||
let v: t = t { a = 5 };
|
||||
let m: maybe = &v;
|
||||
assert(m is *t);
|
||||
assert(!(m is void));
|
||||
let m2: maybe = void;
|
||||
assert(!(m2 is *t));
|
||||
assert(m2 is void);
|
||||
};
|
||||
|
||||
@test fn ident_control() void = {
|
||||
let b: bool = true;
|
||||
let v: val = b;
|
||||
assert(v is bool);
|
||||
assert(!(v is i64));
|
||||
};
|
||||
102
test/lang/match_nonident_idx_test.ww
Normal file
102
test/lang/match_nonident_idx_test.ww
Normal file
@@ -0,0 +1,102 @@
|
||||
// match_nonident_idx_test — match on an N_INDEX scrutinee whose base is a
|
||||
// non-ident (struct-field slice, chained-dot slice, 56B-slot regex-inst union),
|
||||
// plus ident/ident-base controls. Pins arm-order independence and the stamped-
|
||||
// carrier resolve at any base depth. Migrated from
|
||||
// test/wcc/928_match_nonident_idx_run.c (value rows; C driver asserted cs==ww .s).
|
||||
|
||||
package match_nonident_idx_test;
|
||||
|
||||
type va = void;
|
||||
type vb = bool;
|
||||
type vc = size;
|
||||
type tu = (va | vb | vc);
|
||||
|
||||
type holder = struct { xs: []tu, n: size };
|
||||
type inner = struct { xs: []tu, k: size };
|
||||
type outer = struct { in_: inner, n: size };
|
||||
|
||||
type big = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64 };
|
||||
type ilit = struct { r: i64 };
|
||||
type imatch = void;
|
||||
type inst = (big | ilit | imatch);
|
||||
type re = struct { insts: []inst, n: i64 };
|
||||
|
||||
@test fn field_slice_idx() void = {
|
||||
let sl: []tu = [(true: vb), ((7: size): vc)];
|
||||
let h: holder;
|
||||
h.xs = sl;
|
||||
h.n = 2;
|
||||
match (h.xs[0]) {
|
||||
case let b: vb => { assert(b: bool); };
|
||||
case => { assert(false); };
|
||||
};
|
||||
match (h.xs[1]) {
|
||||
case let s: vc => { assert((s: size) == 7); };
|
||||
case => { assert(false); };
|
||||
};
|
||||
match (h.xs[0]) {
|
||||
case va => { assert(false); };
|
||||
case let b: vb => { assert(b: bool); };
|
||||
case vc => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn regex_inst_56b() void = {
|
||||
let sl: []inst;
|
||||
let l: ilit = ilit { r = 65 };
|
||||
let v: inst = l;
|
||||
append(sl, v);
|
||||
let m: inst = void: imatch;
|
||||
append(sl, m);
|
||||
let r: re;
|
||||
r.insts = sl;
|
||||
r.n = 2;
|
||||
match (r.insts[0]) {
|
||||
case let x: ilit => { assert(x.r == 65); };
|
||||
case => { assert(false); };
|
||||
};
|
||||
match (r.insts[1]) {
|
||||
case imatch => { };
|
||||
case => { assert(false); };
|
||||
};
|
||||
match (r.insts[0]) {
|
||||
case big => { assert(false); };
|
||||
case let x: ilit => { assert(x.r == 65); };
|
||||
case imatch => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn dot_depth_idx() void = {
|
||||
let sl: []tu = [(true: vb), ((9: size): vc)];
|
||||
let o: outer;
|
||||
o.in_.xs = sl;
|
||||
o.in_.k = 1;
|
||||
o.n = 2;
|
||||
match (o.in_.xs[0]) {
|
||||
case let b: vb => { assert(b: bool); };
|
||||
case => { assert(false); };
|
||||
};
|
||||
match (o.in_.xs[1]) {
|
||||
case va => { assert(false); };
|
||||
case let s: vc => { assert((s: size) == 9); };
|
||||
case vb => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn ident_and_identbase_controls() void = {
|
||||
let v: tu = (true: vb);
|
||||
match (v) {
|
||||
case let b: vb => { assert(b: bool); };
|
||||
case => { assert(false); };
|
||||
};
|
||||
let arr: [2]tu = [(true: vb), ((7: size): vc)];
|
||||
match (arr[1]) {
|
||||
case let s: vc => { assert((s: size) == 7); };
|
||||
case => { assert(false); };
|
||||
};
|
||||
let sl: []tu = [(true: vb), ((7: size): vc)];
|
||||
match (sl[0]) {
|
||||
case let b: vb => { assert(b: bool); };
|
||||
case => { assert(false); };
|
||||
};
|
||||
};
|
||||
116
test/lang/match_slice_variant_test.ww
Normal file
116
test/lang/match_slice_variant_test.ww
Normal file
@@ -0,0 +1,116 @@
|
||||
// match_slice_variant_test — a `(scalar | []T)` match must select the right arm
|
||||
// and preserve the slice payload (len + first byte) across u8/i8/i32/u64/rune,
|
||||
// in both variant orders, and compose with a trailing str arm. Migrated from
|
||||
// test/wcc/926_match_slice_variant_run.c (value rows, cs==ww byte-id).
|
||||
|
||||
package match_slice_variant_test;
|
||||
|
||||
fn pick_u8(n: (u8 | []u8)) i32 = {
|
||||
match (n) {
|
||||
case let c: u8 => return c: i32;
|
||||
case let s: []u8 => return 100i32 + (s.len: i32) + (s[0]: i32);
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn pick_slice_first(n: ([]u8 | u8)) i32 = {
|
||||
match (n) {
|
||||
case let s: []u8 => return 100i32 + (s.len: i32) + (s[0]: i32);
|
||||
case let c: u8 => return c: i32;
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn pick_i8(n: (i8 | []i8)) i32 = {
|
||||
match (n) {
|
||||
case let c: i8 => return c: i32;
|
||||
case let s: []i8 => return 100i32 + (s.len: i32) + (s[0]: i32);
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn pick_i32(n: (i32 | []i32)) i32 = {
|
||||
match (n) {
|
||||
case let c: i32 => return c;
|
||||
case let s: []i32 => return 1000i32 + (s.len: i32) + s[0];
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn pick_u64(n: (u64 | []u64)) i32 = {
|
||||
match (n) {
|
||||
case let c: u64 => return c: i32;
|
||||
case let s: []u64 => return 1000i32 + (s.len: i32) + (s[0]: i32);
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn pick_rune(n: (rune | []rune)) i32 = {
|
||||
match (n) {
|
||||
case let r: rune => return r: i32;
|
||||
case let s: []rune => return 1000i32 + (s.len: i32) + (s[0]: i32);
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
fn pick3(n: (u8 | []u8 | str)) i32 = {
|
||||
match (n) {
|
||||
case let c: u8 => return c: i32;
|
||||
case let s: []u8 => return 200i32 + (s.len: i32) + (s[0]: i32);
|
||||
case let t: str => return 300i32 + (t.len: i32);
|
||||
};
|
||||
return -1;
|
||||
};
|
||||
|
||||
@test fn u8_slice_u8() void = {
|
||||
let buf: [3]u8;
|
||||
buf[0] = 7u8; buf[1] = 8u8; buf[2] = 9u8;
|
||||
assert(pick_u8(42u8) == 42);
|
||||
assert(pick_u8(buf[0:3]) == 110);
|
||||
};
|
||||
|
||||
@test fn slice_u8_then_u8() void = {
|
||||
let buf: [3]u8;
|
||||
buf[0] = 7u8; buf[1] = 8u8; buf[2] = 9u8;
|
||||
assert(pick_slice_first(buf[0:3]) == 110);
|
||||
assert(pick_slice_first(42u8) == 42);
|
||||
};
|
||||
|
||||
@test fn i8_slice_i8() void = {
|
||||
let buf: [2]i8;
|
||||
buf[0] = 5i8; buf[1] = 6i8;
|
||||
assert(pick_i8(11i8) == 11);
|
||||
assert(pick_i8(buf[0:2]) == 107);
|
||||
};
|
||||
|
||||
@test fn i32_slice_i32() void = {
|
||||
let buf: [2]i32;
|
||||
buf[0] = 33i32; buf[1] = 44i32;
|
||||
assert(pick_i32(77i32) == 77);
|
||||
assert(pick_i32(buf[0:2]) == 1035);
|
||||
};
|
||||
|
||||
@test fn u64_slice_u64() void = {
|
||||
let buf: [2]u64;
|
||||
buf[0] = 41u64; buf[1] = 42u64;
|
||||
assert(pick_u64(99u64) == 99);
|
||||
assert(pick_u64(buf[0:2]) == 1043);
|
||||
};
|
||||
|
||||
@test fn rune_slice_rune() void = {
|
||||
let buf: [2]rune;
|
||||
buf[0] = 'a'; buf[1] = 'b';
|
||||
assert(pick_rune('Z') == 90);
|
||||
assert(pick_rune(buf[0:2]) == 1099);
|
||||
};
|
||||
|
||||
@test fn u8_slice_u8_str() void = {
|
||||
let buf: [3]u8;
|
||||
buf[0] = 1u8; buf[1] = 2u8; buf[2] = 3u8;
|
||||
let a: i32 = pick3(5u8);
|
||||
let b: i32 = pick3(buf[0:3]);
|
||||
let c: i32 = pick3("hi");
|
||||
assert(a == 5);
|
||||
assert(b == 204);
|
||||
assert(c == 302);
|
||||
};
|
||||
58
test/lang/nested_union_widen_test.ww
Normal file
58
test/lang/nested_union_widen_test.ww
Normal file
@@ -0,0 +1,58 @@
|
||||
// nested_union_widen_test — widening a nested named error-union (inner = !(i32|bool))
|
||||
// into an enclosing (size | inner) union must keep the inner box distinguishable
|
||||
// and destructurable across outer-arm select, fn-return, and let-init. Migrated
|
||||
// from test/wcc/925_nested_union_widen_run.c (value rows). The same-shape
|
||||
// collision row (two distinct names !(i32|bool)) loud-stops both stages and lives
|
||||
// at test/wcc/data/nestunion_collision/ as a runww //ww:error carrier.
|
||||
|
||||
package nested_union_widen_test;
|
||||
|
||||
type inner = !(i32 | bool);
|
||||
|
||||
fn g_dr() (size | inner) = {
|
||||
let e: inner = (5i32: inner);
|
||||
return e;
|
||||
};
|
||||
|
||||
@test fn outer_arm_select() void = {
|
||||
let e: inner = (true: inner);
|
||||
let r: (size | inner) = e;
|
||||
match (r) {
|
||||
case let n: size => { assert(false); };
|
||||
case let x: inner => { };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn destructure_return() void = {
|
||||
match (g_dr()) {
|
||||
case let s: size => { assert(false); };
|
||||
case let x: inner => {
|
||||
match (x) {
|
||||
case let i: i32 => { assert(i == 5); };
|
||||
case let b: bool => { assert(false); };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@test fn destructure_let() void = {
|
||||
let e: inner = (7i32: inner);
|
||||
let r: (size | inner) = e;
|
||||
match (r) {
|
||||
case let n: size => { assert(false); };
|
||||
case let x: inner => {
|
||||
match (x) {
|
||||
case let i: i32 => { assert(i == 7); };
|
||||
case let b: bool => { assert(false); };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@test fn single_variant() void = {
|
||||
let r: (i32 | bool) = 7i32;
|
||||
match (r) {
|
||||
case let n: i32 => { assert(n == 7); };
|
||||
case let b: bool => { assert(false); };
|
||||
};
|
||||
};
|
||||
70
test/lang/tagged_call_arg_test.ww
Normal file
70
test/lang/tagged_call_arg_test.ww
Normal file
@@ -0,0 +1,70 @@
|
||||
// tagged_call_arg_test — #21: passing a tagged-union arg sourced from a fn call
|
||||
// that returns the same tagged union must not drop the DX/CX/R8 payload words.
|
||||
// Pins CALL-source (4-variant + 2-variant) and the IDENT-source fast-path that
|
||||
// must stay byte-identical, plus a tagged-CALL-then-scalar pop-count row.
|
||||
// Migrated from test/wcc/924_tagged_call_arg_run.c (value rows, cs==ww).
|
||||
|
||||
package tagged_call_arg_test;
|
||||
|
||||
type done = !void;
|
||||
type more = !void;
|
||||
type invalid = !void;
|
||||
type oserror = !i32;
|
||||
|
||||
fn yield_rune() (rune | done | more | invalid) = {
|
||||
return 65u32: rune;
|
||||
};
|
||||
fn dispatch4(v: (rune | done | more | invalid)) i32 = {
|
||||
match (v) {
|
||||
case let r: rune => return r: i32;
|
||||
case let d: done => return -1;
|
||||
case let m: more => return -2;
|
||||
case let e: invalid => return -3;
|
||||
};
|
||||
return -99;
|
||||
};
|
||||
|
||||
fn yield_ptr() (*u8 | oserror) = {
|
||||
let p: *u8 = nil;
|
||||
return p;
|
||||
};
|
||||
fn yield_err() (*u8 | oserror) = {
|
||||
let e: oserror = 42i32: oserror;
|
||||
return e;
|
||||
};
|
||||
fn dispatch2(v: (*u8 | oserror)) i32 = {
|
||||
match (v) {
|
||||
case let p: *u8 => return 7;
|
||||
case let e: oserror => return e: i32;
|
||||
};
|
||||
return -99;
|
||||
};
|
||||
|
||||
fn use2(v: (*u8 | oserror), k: i32) i32 = {
|
||||
match (v) {
|
||||
case let p: *u8 => return k + 1;
|
||||
case let e: oserror => return k - 1;
|
||||
};
|
||||
return -99;
|
||||
};
|
||||
|
||||
@test fn variant4_call_source_rune() void = {
|
||||
assert(dispatch4(yield_rune()) == 65);
|
||||
};
|
||||
|
||||
@test fn variant4_ident_source_rune() void = {
|
||||
let v: (rune | done | more | invalid) = 65u32: rune;
|
||||
assert(dispatch4(v) == 65);
|
||||
};
|
||||
|
||||
@test fn variant2_call_source_ptr() void = {
|
||||
assert(dispatch2(yield_ptr()) == 7);
|
||||
};
|
||||
|
||||
@test fn variant2_call_source_err() void = {
|
||||
assert(dispatch2(yield_err()) == 42);
|
||||
};
|
||||
|
||||
@test fn tagged_call_then_scalar() void = {
|
||||
assert(use2(yield_ptr(), 41) == 42);
|
||||
};
|
||||
378
test/lang/tagged_structlit_payload_test.ww
Normal file
378
test/lang/tagged_structlit_payload_test.ww
Normal file
@@ -0,0 +1,378 @@
|
||||
// tagged_structlit_payload_test — #23/#33/#38a: constructing a tagged union from
|
||||
// an inline struct literal whose fields are themselves tagged (void|size) /
|
||||
// (void|str) must fill the payload at the WIDENED slot's payload base, copy every
|
||||
// payload word (not just AX), and read/assign tagged leaves down a dot-spine.
|
||||
// Migrated from test/wcc/938_tagged_structlit_payload_run.c (value rows, cs==ww
|
||||
// byte-id). Types are per-row-prefixed because the C rows reused names with
|
||||
// different shapes.
|
||||
//
|
||||
// voidstr_field + recursion_torture do an inner match on a tagged FIELD scrutinee
|
||||
// (o.s, m.t) — historically tagged "match-on-tagged-struct-FIELD cs≠ww" (aliases
|
||||
// regex-arc #26 / cont.9 #42 match-field-cursor); the retired C driver flagged
|
||||
// them noasm=1. Divergence tag (master 6699158) STALE at HEAD: byte-id verified
|
||||
// cs==ww AND T1 value-correct. These rows are the REGRESSION SENTINEL for the
|
||||
// match-on-tagged-field divergence (former #26), closed no-reproducer-at-HEAD with
|
||||
// attribution to the #15/#17 tagged-field-via-ptr cluster INFERRED (not diff-
|
||||
// proven) — a resurgence trips this byte-id row. Reopen with a concrete shape if
|
||||
// it fails.
|
||||
|
||||
package tagged_structlit_payload_test;
|
||||
|
||||
type ir_lit = rune;
|
||||
type ir_rep = struct { id: size, origin: size, min: (void | size), max: (void | size) };
|
||||
type ir_inst = (ir_lit | ir_rep);
|
||||
|
||||
type ra_lit = rune;
|
||||
type ra_rep3 = struct { id: size, min: (void | size), max: (void | size) };
|
||||
type ra_inst = (ra_lit | ra_rep3);
|
||||
|
||||
type to_only = struct { m: (void | size) };
|
||||
type to_u = (rune | to_only);
|
||||
|
||||
type pb_lit = rune;
|
||||
type pb_rep3 = struct { id: size, min: (void | size), max: (void | size) };
|
||||
type pb_inst = (pb_lit | pb_rep3);
|
||||
|
||||
type sat_lit = rune;
|
||||
type sat_named = struct { s: str, m: (void | size) };
|
||||
type sat_inst = (sat_lit | sat_named);
|
||||
|
||||
type vp_lit = rune;
|
||||
type vp_rep3 = struct { id: size, min: (void | size), max: (void | size) };
|
||||
type vp_inst = (vp_rep3 | vp_lit);
|
||||
|
||||
type sec_lit = rune;
|
||||
type sec_rep1 = struct { id: size, m: (void | size) };
|
||||
type sec_inst = (sec_lit | sec_rep1);
|
||||
|
||||
type nsf_inner = struct { a: size, b: size };
|
||||
type nsf_outer = struct { id: size, in_: nsf_inner };
|
||||
type nsf_u = (rune | nsf_outer);
|
||||
|
||||
type ea_st = struct { a: u64, m: (void | size) };
|
||||
|
||||
type tto_d2 = struct { m: (void | size), n: (void | i64), z: u64 };
|
||||
|
||||
type nat_inn = struct { v: u64 };
|
||||
type nat_outt = struct { i: nat_inn, m: (void | size) };
|
||||
|
||||
type pb8_one = struct { a: size };
|
||||
type pb8_u = (rune | pb8_one);
|
||||
|
||||
type sn_plain = struct { a: size, f: f64, s: str };
|
||||
type sn_u = (rune | sn_plain);
|
||||
|
||||
type clr_rep = struct { id: size, min: (void | size), name: (void | str) };
|
||||
type clr_outer = struct { tag: size, r: clr_rep };
|
||||
|
||||
type cas_rep = struct { id: size, min: (void | size) };
|
||||
type cas_outer = struct { tag: size, r: cas_rep };
|
||||
|
||||
type cd2_rep = struct { id: size, min: (void | size) };
|
||||
type cd2_mid = struct { m: size, r: cd2_rep };
|
||||
type cd2_outer = struct { tag: size, w: cd2_mid };
|
||||
|
||||
type csv_rep = struct { id: size, nm: (void | str) };
|
||||
type csv_outer = struct { tag: size, r: csv_rep };
|
||||
|
||||
type cpr_rep = struct { id: size, min: (void | size) };
|
||||
type cpr_outer = struct { tag: size, r: cpr_rep };
|
||||
|
||||
type cpm_rep = struct { id: size, min: (void | size) };
|
||||
type cpm_outer = struct { tag: size, p: *cpm_rep };
|
||||
|
||||
type vsf_st = struct { id: size, s: (void | str) };
|
||||
type vsf_u = (rune | vsf_st);
|
||||
|
||||
type leaf = struct { p: size };
|
||||
type t1 = (void | leaf);
|
||||
type rt_mid = struct { t: t1 };
|
||||
type t2 = (void | rt_mid);
|
||||
type rt_outr = struct { id: size, t: t2 };
|
||||
type rt_u = (rune | rt_outr);
|
||||
|
||||
fn take_uib(v: (void | size)) i32 = {
|
||||
if (v is size) { return (v as size): i32; };
|
||||
return -1;
|
||||
};
|
||||
|
||||
@test fn inst_repeat_shape() void = {
|
||||
let v: ir_inst = ir_rep { id = 1: size, origin = 2: size, min = void, max = 5: size };
|
||||
match (v) {
|
||||
case let ir: ir_rep => {
|
||||
assert(ir.id == 1);
|
||||
assert(ir.origin == 2);
|
||||
assert(ir.min is void);
|
||||
assert(ir.max is size);
|
||||
assert(ir.max as size == 5);
|
||||
};
|
||||
case let l: ir_lit => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn rep3_allsize() void = {
|
||||
let v: ra_inst = ra_rep3 { id = 1: size, min = 2: size, max = 5: size };
|
||||
match (v) {
|
||||
case let ir: ra_rep3 => {
|
||||
assert(ir.id == 1);
|
||||
assert(ir.min is size);
|
||||
assert(ir.min as size == 2);
|
||||
assert(ir.max is size);
|
||||
assert(ir.max as size == 5);
|
||||
};
|
||||
case let l: ra_lit => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn tagged_only16() void = {
|
||||
let v: to_u = to_only { m = 7: size };
|
||||
match (v) {
|
||||
case let o: to_only => {
|
||||
assert(o.m is size);
|
||||
assert(o.m as size == 7);
|
||||
};
|
||||
case => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn prebound_tagged_field() void = {
|
||||
let mn: (void | size) = 2: size;
|
||||
let v: pb_inst = pb_rep3 { id = 3: size, min = mn, max = void };
|
||||
match (v) {
|
||||
case let ir: pb_rep3 => {
|
||||
assert(ir.id == 3);
|
||||
assert(ir.min is size);
|
||||
assert(ir.min as size == 2);
|
||||
assert(ir.max is void);
|
||||
};
|
||||
case let l: pb_lit => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn str_and_tagged() void = {
|
||||
let v: sat_inst = sat_named { s = "hi", m = 9: size };
|
||||
match (v) {
|
||||
case let nm: sat_named => {
|
||||
assert(len(nm.s) == 2);
|
||||
assert(nm.m is size);
|
||||
assert(nm.m as size == 9);
|
||||
};
|
||||
case let l: sat_lit => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn variant_pos0() void = {
|
||||
let v: vp_inst = vp_rep3 { id = 1: size, min = void, max = 8: size };
|
||||
match (v) {
|
||||
case let ir: vp_rep3 => {
|
||||
assert(ir.id == 1);
|
||||
assert(ir.min is void);
|
||||
assert(ir.max is size);
|
||||
assert(ir.max as size == 8);
|
||||
};
|
||||
case let l: vp_lit => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn slice_elem_cursorfit() void = {
|
||||
let insts: []sec_inst = [];
|
||||
append(insts, ('a': sec_lit));
|
||||
let v: sec_inst = sec_rep1 { id = 4: size, m = 6: size };
|
||||
append(insts, v);
|
||||
assert(len(insts) == 2);
|
||||
match (insts[1]) {
|
||||
case let r: sec_rep1 => {
|
||||
assert(r.id == 4);
|
||||
assert(r.m is size);
|
||||
assert(r.m as size == 6);
|
||||
};
|
||||
case let l: sec_lit => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn nested_struct_field() void = {
|
||||
let v: nsf_u = nsf_outer { id = 1: size, in_ = nsf_inner { a = 2: size, b = 3: size } };
|
||||
match (v) {
|
||||
case let o: nsf_outer => {
|
||||
assert(o.id == 1);
|
||||
assert(o.in_.a == 2);
|
||||
assert(o.in_.b == 3);
|
||||
};
|
||||
case => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn ellipsis_autofill() void = {
|
||||
let e: (void | ea_st) = ea_st { m = 5: size, ... };
|
||||
match (e) {
|
||||
case let s: ea_st => {
|
||||
assert(s.a == 0u64);
|
||||
assert(s.m is size);
|
||||
assert(s.m as size == 5);
|
||||
};
|
||||
case void => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn two_tagged_offset0() void = {
|
||||
let e: (void | tto_d2) = tto_d2 { m = 4: size, n = (-9): i64, z = 77u64 };
|
||||
match (e) {
|
||||
case let s: tto_d2 => {
|
||||
assert(s.m is size);
|
||||
assert(s.m as size == 4);
|
||||
assert(s.n is i64);
|
||||
assert(s.n as i64 == -9);
|
||||
assert(s.z == 77u64);
|
||||
};
|
||||
case void => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn nested_and_tagged() void = {
|
||||
let e: (void | nat_outt) = nat_outt { i = nat_inn { v = 11u64 }, m = 8: size };
|
||||
match (e) {
|
||||
case let s: nat_outt => {
|
||||
assert(s.i.v == 11u64);
|
||||
assert(s.m is size);
|
||||
assert(s.m as size == 8);
|
||||
};
|
||||
case void => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn payload8_boundary() void = {
|
||||
let v: pb8_u = pb8_one { a = 42: size };
|
||||
match (v) {
|
||||
case let o: pb8_one => { assert(o.a == 42); };
|
||||
case => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn scalar_neighbor() void = {
|
||||
let v: sn_u = sn_plain { a = 11: size, f = 2.5, s = "xyz" };
|
||||
match (v) {
|
||||
case let p: sn_plain => {
|
||||
assert(p.a == 11);
|
||||
assert(p.f == 2.5);
|
||||
assert(len(p.s) == 3);
|
||||
};
|
||||
case => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn chained_tagged_literal_read() void = {
|
||||
let o = clr_outer { tag = 4: size, r = clr_rep { id = 6: size, min = 8: size, name = void } };
|
||||
assert(o.tag == 4);
|
||||
assert(o.r.id == 6);
|
||||
assert(o.r.min is size);
|
||||
assert(o.r.min as size == 8);
|
||||
assert(o.r.name is void);
|
||||
};
|
||||
|
||||
@test fn chained_tagged_assign() void = {
|
||||
let o: cas_outer = cas_outer { tag = 4: size, r = cas_rep { id = 6: size, min = void } };
|
||||
o.r.min = 8: size;
|
||||
assert(o.r.min is size);
|
||||
assert(o.r.min as size == 8);
|
||||
o.r.min = 9;
|
||||
assert(o.r.min is size);
|
||||
assert(o.r.min as size == 9);
|
||||
};
|
||||
|
||||
@test fn chained_tagged_depth2() void = {
|
||||
let o = cd2_outer { tag = 4: size, w = cd2_mid { m = 2: size, r = cd2_rep { id = 6: size, min = 5: size } } };
|
||||
assert(o.w.r.min is size);
|
||||
assert(o.w.r.min as size == 5);
|
||||
o.w.r.min = 8: size;
|
||||
assert(o.w.m == 2);
|
||||
assert(o.w.r.id == 6);
|
||||
assert(o.w.r.min is size);
|
||||
assert(o.w.r.min as size == 8);
|
||||
};
|
||||
|
||||
@test fn chained_tagged_strvariant() void = {
|
||||
let o = csv_outer { tag = 4: size, r = csv_rep { id = 6: size, nm = void } };
|
||||
o.r.nm = "hello";
|
||||
assert(o.r.nm is str);
|
||||
assert((o.r.nm as str).len == 5);
|
||||
match (o.r.nm) {
|
||||
case let s: str => { assert(s.len == 5); };
|
||||
case void => { assert(false); };
|
||||
};
|
||||
o.r.nm = void;
|
||||
assert(o.r.nm is void);
|
||||
assert(o.r.id == 6);
|
||||
};
|
||||
|
||||
@test fn chained_tagged_ptrroot() void = {
|
||||
let o = cpr_outer { tag = 4: size, r = cpr_rep { id = 6: size, min = void } };
|
||||
let p: *cpr_outer = &o;
|
||||
p.r.min = 8: size;
|
||||
assert(p.r.id == 6);
|
||||
assert(p.r.min is size);
|
||||
assert(p.r.min as size == 8);
|
||||
};
|
||||
|
||||
@test fn chained_tagged_ptrmid() void = {
|
||||
let r = cpm_rep { id = 6: size, min = void };
|
||||
let o = cpm_outer { tag = 4: size, p = &r };
|
||||
o.p.min = 8: size;
|
||||
assert(o.p.id == 6);
|
||||
assert(o.p.min is size);
|
||||
assert(o.p.min as size == 8);
|
||||
};
|
||||
|
||||
@test fn untyped_int_bare_widen() void = {
|
||||
let e: (void | size) = 5;
|
||||
assert(e is size);
|
||||
assert(e as size == 5);
|
||||
let mn: (void | size) = void;
|
||||
mn = 7;
|
||||
assert(mn is size);
|
||||
assert(mn as size == 7);
|
||||
let b: (bool | u64) = 9;
|
||||
assert(b is u64);
|
||||
assert(take_uib(9) == 9);
|
||||
let cf: (void | size) = 5: size;
|
||||
assert(cf is size);
|
||||
assert(cf as size == 5);
|
||||
let f: (void | i64) = 9;
|
||||
assert(f is i64);
|
||||
assert(f as i64 == 9);
|
||||
};
|
||||
|
||||
// voidstr_field/recursion_torture: regression-sentinel rows for the former-#26
|
||||
// match-on-tagged-field divergence (STALE at HEAD: byte-id cs==ww + value-correct;
|
||||
// closed no-reproducer, #15/#17 attribution inferred). See the file header.
|
||||
@test fn voidstr_field() void = {
|
||||
let v: vsf_u = vsf_st { id = 3: size, s = "hello" };
|
||||
match (v) {
|
||||
case let o: vsf_st => {
|
||||
assert(o.id == 3);
|
||||
match (o.s) {
|
||||
case let sv: str => { assert(len(sv) == 5); };
|
||||
case void => { assert(false); };
|
||||
};
|
||||
};
|
||||
case => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn recursion_torture() void = {
|
||||
let v: rt_u = rt_outr { id = 1: size, t = rt_mid { t = leaf { p = 9: size } } };
|
||||
match (v) {
|
||||
case let o: rt_outr => {
|
||||
assert(o.id == 1);
|
||||
match (o.t) {
|
||||
case let m: rt_mid => {
|
||||
match (m.t) {
|
||||
case let l: leaf => { assert(l.p == 9); };
|
||||
case void => { assert(false); };
|
||||
};
|
||||
};
|
||||
case void => { assert(false); };
|
||||
};
|
||||
};
|
||||
case => { assert(false); };
|
||||
};
|
||||
};
|
||||
73
test/lang/tagged_widen_arg_test.ww
Normal file
73
test/lang/tagged_widen_arg_test.ww
Normal file
@@ -0,0 +1,73 @@
|
||||
// tagged_widen_arg_test — a narrower tagged source (ident/deref/index/dot) passed
|
||||
// into a wider tagged param must widen + tag-remap so the callee reads the right
|
||||
// arm. The aligned set keeps i64 at tag0 in both; the remap set has i64 at tag1
|
||||
// in the source (un16b) but tag0 in the param (un3b), forcing the remap.
|
||||
// Migrated from test/wcc/944_tagged_widen_arg_run.c (value rows, all cs==ww byte-id).
|
||||
|
||||
package tagged_widen_arg_test;
|
||||
|
||||
type s16 = struct { a: i64, b: i64 };
|
||||
type un16 = (i64 | bool);
|
||||
type un3 = (i64 | bool | s16);
|
||||
type un16b = (bool | i64);
|
||||
type un3b = (i64 | bool | s16);
|
||||
type wrap_a = struct { f: un16 };
|
||||
type wrap_b = struct { f: un16b };
|
||||
|
||||
fn take_aligned(e: un3) i64 = {
|
||||
match (e) {
|
||||
case let v: i64 => { return v; };
|
||||
case let b: bool => { return -1; };
|
||||
case let s: s16 => { return s.a + s.b; };
|
||||
};
|
||||
};
|
||||
|
||||
fn take_remap(e: un3b) i64 = {
|
||||
match (e) {
|
||||
case let v: i64 => { return v; };
|
||||
case let b: bool => { return -1; };
|
||||
case let s: s16 => { return s.a + s.b; };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn ident_aligned() void = {
|
||||
let v: un16 = (42: i64);
|
||||
assert(take_aligned(v) == 42);
|
||||
};
|
||||
|
||||
@test fn deref_aligned() void = {
|
||||
let v: un16 = (42: i64);
|
||||
let p: *un16 = &v;
|
||||
assert(take_aligned(*p) == 42);
|
||||
};
|
||||
|
||||
@test fn index_aligned() void = {
|
||||
let xs: [2]un16 = [(42: i64), (7: i64)];
|
||||
assert(take_aligned(xs[0]) == 42);
|
||||
};
|
||||
|
||||
@test fn dot_aligned() void = {
|
||||
let w: wrap_a = wrap_a { f = (42: i64) };
|
||||
assert(take_aligned(w.f) == 42);
|
||||
};
|
||||
|
||||
@test fn ident_remap() void = {
|
||||
let v: un16b = (42: i64);
|
||||
assert(take_remap(v) == 42);
|
||||
};
|
||||
|
||||
@test fn deref_remap() void = {
|
||||
let v: un16b = (42: i64);
|
||||
let p: *un16b = &v;
|
||||
assert(take_remap(*p) == 42);
|
||||
};
|
||||
|
||||
@test fn index_remap() void = {
|
||||
let xs: [2]un16b = [(42: i64), (7: i64)];
|
||||
assert(take_remap(xs[0]) == 42);
|
||||
};
|
||||
|
||||
@test fn dot_remap() void = {
|
||||
let w: wrap_b = wrap_b { f = (42: i64) };
|
||||
assert(take_remap(w.f) == 42);
|
||||
};
|
||||
67
test/lang/taggedderefstore_test.ww
Normal file
67
test/lang/taggedderefstore_test.ww
Normal file
@@ -0,0 +1,67 @@
|
||||
// taggedderefstore_test — #17 teeth: `*p = v` into a tagged-union pointee must
|
||||
// store the full box (route through the widener + scratch word-copy), not a
|
||||
// single tag-word store. Was both-stage-wrong AND byte-id-green (#263-class),
|
||||
// now CLOSED both stages. Migrated from test/wcc/989_taggedderefstore_run.c
|
||||
// (value rows; scalarctl pins the non-tagged single-store path stays unchanged).
|
||||
|
||||
package taggedderefstore_test;
|
||||
|
||||
type pair = struct { a: i64, b: i64 };
|
||||
|
||||
@test fn taglo() void = {
|
||||
let v: (int | bool) = false;
|
||||
let p: *(int | bool) = &v;
|
||||
*p = 42;
|
||||
match (v) {
|
||||
case let n: int => { assert(n == 42); };
|
||||
case bool => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn taghi() void = {
|
||||
let v: (int | bool) = 0;
|
||||
let p: *(int | bool) = &v;
|
||||
*p = true;
|
||||
match (v) {
|
||||
case int => { assert(false); };
|
||||
case let b: bool => { assert(b); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn str3w() void = {
|
||||
let v: (int | str) = 0;
|
||||
let p: *(int | str) = &v;
|
||||
*p = "hello";
|
||||
match (v) {
|
||||
case int => { assert(false); };
|
||||
case let s: str => { assert(s.len: int == 5); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn struct2w() void = {
|
||||
let v: (int | pair) = 0;
|
||||
let p: *(int | pair) = &v;
|
||||
*p = pair { a = 0x1111, b = 0x2222 };
|
||||
match (v) {
|
||||
case int => { assert(false); };
|
||||
case let q: pair => { assert(q.a == 0x1111 && q.b == 0x2222); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn nullfold() void = {
|
||||
let a: int = 7;
|
||||
let v: (*int | void) = void;
|
||||
let p: *(*int | void) = &v;
|
||||
*p = &a;
|
||||
match (v) {
|
||||
case let q: *int => { assert(*q == 7); };
|
||||
case void => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn scalarctl() void = {
|
||||
let v: int = 3;
|
||||
let p: *int = &v;
|
||||
*p = 42;
|
||||
assert(v == 42);
|
||||
};
|
||||
71
test/lang/taggedglobalindex_test.ww
Normal file
71
test/lang/taggedglobalindex_test.ww
Normal file
@@ -0,0 +1,71 @@
|
||||
// taggedglobalindex_test — #16 teeth: `gs[i] = v` into a global tagged array
|
||||
// must use the global base g(SB) and store the full box. Migrated from
|
||||
// test/wcc/989_taggedglobalindex_run.c (value rows; loc_ctl/scalar_ctl pin the
|
||||
// frame-base and scalar-array control arms stay unperturbed). Globals kept at
|
||||
// module scope.
|
||||
|
||||
package taggedglobalindex_test;
|
||||
|
||||
type pair = struct { a: i64, b: i64 };
|
||||
|
||||
let gs_lo: [2](int | bool) = [0, 0];
|
||||
let gs_hi: [2](int | bool) = [0, 0];
|
||||
let gs_var: [3](int | bool) = [0, 0, 0];
|
||||
let gs_str: [2](int | str) = [0, 0];
|
||||
let gs_struct: [2](int | pair) = [0, 0];
|
||||
let g_scalar: [3]int = [0, 0, 0];
|
||||
|
||||
@test fn glo_taglo() void = {
|
||||
gs_lo[1] = 42;
|
||||
match (gs_lo[1]) {
|
||||
case let n: int => { assert(n == 42); };
|
||||
case bool => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn glo_taghi() void = {
|
||||
gs_hi[1] = true;
|
||||
match (gs_hi[1]) {
|
||||
case int => { assert(false); };
|
||||
case let b: bool => { assert(b); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn glo_varidx() void = {
|
||||
let i: int = 2;
|
||||
gs_var[i] = 99;
|
||||
match (gs_var[2]) {
|
||||
case let n: int => { assert(n == 99); };
|
||||
case bool => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn glo_str3w() void = {
|
||||
gs_str[1] = "hello";
|
||||
match (gs_str[1]) {
|
||||
case int => { assert(false); };
|
||||
case let s: str => { assert(s.len: int == 5); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn glo_struct2w() void = {
|
||||
gs_struct[1] = pair { a = 0x1111, b = 0x2222 };
|
||||
match (gs_struct[1]) {
|
||||
case int => { assert(false); };
|
||||
case let q: pair => { assert(q.a == 0x1111 && q.b == 0x2222); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn loc_ctl() void = {
|
||||
let a: [2](int | bool) = [0, 0];
|
||||
a[1] = 42;
|
||||
match (a[1]) {
|
||||
case let n: int => { assert(n == 42); };
|
||||
case bool => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn scalar_ctl() void = {
|
||||
g_scalar[1] = 42;
|
||||
assert(g_scalar[1] == 42);
|
||||
};
|
||||
48
test/lang/taggedidx_test.ww
Normal file
48
test/lang/taggedidx_test.ww
Normal file
@@ -0,0 +1,48 @@
|
||||
// taggedidx_test — #23: a tagged element off a non-ident base (slice `arr[1:][0]`,
|
||||
// call `getrows()[1]`) must load the full cursor, not just the tag word. The
|
||||
// N_IDENT base control already worked and must not regress. Migrated from
|
||||
// test/wcc/989_taggedidx_run.c (value rows).
|
||||
|
||||
package taggedidx_test;
|
||||
|
||||
type box = (i64 | bool);
|
||||
|
||||
fn getrows() []box = {
|
||||
let arr: [3]box;
|
||||
arr[0] = 10; arr[1] = 50; arr[2] = 70;
|
||||
return arr[0:];
|
||||
};
|
||||
|
||||
@test fn slice_tagged_idx() void = {
|
||||
let arr: [3]box;
|
||||
arr[0] = 10; arr[1] = 40; arr[2] = 70;
|
||||
let v: box = arr[1:][0];
|
||||
let r: i64 = 0;
|
||||
match (v) {
|
||||
case let x: i64 => { r = x; };
|
||||
case let b: bool => { r = 99; };
|
||||
};
|
||||
assert(r == 40);
|
||||
};
|
||||
|
||||
@test fn call_tagged_idx() void = {
|
||||
let v: box = getrows()[1];
|
||||
let r: i64 = 0;
|
||||
match (v) {
|
||||
case let x: i64 => { r = x; };
|
||||
case let b: bool => { r = 99; };
|
||||
};
|
||||
assert(r == 50);
|
||||
};
|
||||
|
||||
@test fn ident_tagged_idx() void = {
|
||||
let arr: [3]box;
|
||||
arr[0] = 10; arr[1] = 40; arr[2] = 70;
|
||||
let v: box = arr[1];
|
||||
let r: i64 = 0;
|
||||
match (v) {
|
||||
case let x: i64 => { r = x; };
|
||||
case let b: bool => { r = 99; };
|
||||
};
|
||||
assert(r == 40);
|
||||
};
|
||||
80
test/lang/tagnorm_test.ww
Normal file
80
test/lang/tagnorm_test.ww
Normal file
@@ -0,0 +1,80 @@
|
||||
// tagnorm_test — tagged-union normalization: nullable folds to an 8B ptr slot;
|
||||
// `never` drops; duplicate variants dedup; size/align ride the normalized chain;
|
||||
// and a bare `return;` void tag comes off the NORMALIZED (deduped) variant index
|
||||
// at positions 0/1/2. dedup_match pins that a deduped (i32|i32|str) constructs +
|
||||
// matches its str variant in both stages. Migrated from
|
||||
// test/wcc/989_tagnorm_run.c (value rows). dedup_match: the old C driver's comment
|
||||
// claimed its str tag-NUMBER diverged (cs $1 vs ww $2) — that divergence tag is
|
||||
// STALE at HEAD: byte-id verified cs==ww AND T1 value-correct (the @test runs and
|
||||
// asserts r==7), so it migrates as a NORMAL byte-id @test row, not a runonly.
|
||||
|
||||
package tagnorm_test;
|
||||
|
||||
type tn_p = (*u8 | void);
|
||||
type tn_q = (i32 | never);
|
||||
type tn_d = (i32 | i32);
|
||||
type tn_u3str = (i32 | i32 | str);
|
||||
type tn_uvoid = (i32 | i32 | void);
|
||||
type tn_vpos0 = (void | i32);
|
||||
type tn_vpos2 = (i32 | str | void);
|
||||
|
||||
fn f_void_dedup() tn_uvoid = { return; };
|
||||
fn f_void_pos0() tn_vpos0 = { return; };
|
||||
fn f_void_pos2() tn_vpos2 = { return; };
|
||||
|
||||
@test fn nullable_size() void = {
|
||||
assert(size(tn_p): int == 8);
|
||||
};
|
||||
|
||||
@test fn never_size() void = {
|
||||
assert(size(tn_q): int == 4);
|
||||
};
|
||||
|
||||
@test fn dup_size() void = {
|
||||
assert(size(tn_d): int == 4);
|
||||
};
|
||||
|
||||
@test fn never_align() void = {
|
||||
assert(align(tn_q): int == 4);
|
||||
};
|
||||
|
||||
@test fn dedup_match() void = {
|
||||
let v: tn_u3str = "hi";
|
||||
let r: i64 = 0;
|
||||
match (v) {
|
||||
case let s: str => { r = 7; };
|
||||
case let x: i32 => { r = 1; };
|
||||
};
|
||||
assert(r == 7);
|
||||
};
|
||||
|
||||
@test fn void_dedup_ret() void = {
|
||||
let v: tn_uvoid = f_void_dedup();
|
||||
let r: i64 = 0;
|
||||
match (v) {
|
||||
case void => { r = 5; };
|
||||
case let x: i32 => { r = 9; };
|
||||
};
|
||||
assert(r == 5);
|
||||
};
|
||||
|
||||
@test fn void_pos0_ret() void = {
|
||||
let v: tn_vpos0 = f_void_pos0();
|
||||
let r: i64 = 0;
|
||||
match (v) {
|
||||
case void => { r = 5; };
|
||||
case let x: i32 => { r = 9; };
|
||||
};
|
||||
assert(r == 5);
|
||||
};
|
||||
|
||||
@test fn void_pos2_ret() void = {
|
||||
let v: tn_vpos2 = f_void_pos2();
|
||||
let r: i64 = 0;
|
||||
match (v) {
|
||||
case void => { r = 5; };
|
||||
case let s: str => { r = 8; };
|
||||
case let x: i32 => { r = 9; };
|
||||
};
|
||||
assert(r == 5);
|
||||
};
|
||||
82
test/lang/tryprop_tag_remap_test.ww
Normal file
82
test/lang/tryprop_tag_remap_test.ww
Normal file
@@ -0,0 +1,82 @@
|
||||
// tryprop_tag_remap_test — the `?` try-operator must remap variant tags when the
|
||||
// propagated error union's variant ORDER differs between callee and caller
|
||||
// ((i32|e1|e2) propagated through (i32|e2|e1)), and pass multiword (str) error
|
||||
// payloads intact. Migrated from test/wcc/925_tryprop_tag_remap_run.c (value rows).
|
||||
|
||||
package tryprop_tag_remap_test;
|
||||
|
||||
type e1 = !void;
|
||||
type e2 = !void;
|
||||
type emsg = !str;
|
||||
|
||||
fn g(which: i32) (i32 | e1 | e2) = {
|
||||
if (which == 1) { return e1{}; };
|
||||
if (which == 2) { return e2{}; };
|
||||
return 100;
|
||||
};
|
||||
|
||||
// caller variant order (i32|e2|e1) differs from g's (i32|e1|e2): the remap fires.
|
||||
fn f_diff(which: i32) (i32 | e2 | e1) = {
|
||||
let v: i32 = g(which)?;
|
||||
return v + 1;
|
||||
};
|
||||
|
||||
// caller order matches g's: no remap (control).
|
||||
fn f_same(which: i32) (i32 | e1 | e2) = {
|
||||
let v: i32 = g(which)?;
|
||||
return v + 1;
|
||||
};
|
||||
|
||||
fn g_mw(which: i32) (i32 | e1 | emsg) = {
|
||||
if (which == 1) { return e1{}; };
|
||||
if (which == 2) { let m: emsg = "AB": emsg; return m; };
|
||||
return 100;
|
||||
};
|
||||
fn f_mw(which: i32) (i32 | emsg | e1) = {
|
||||
let v: i32 = g_mw(which)?;
|
||||
return v + 1;
|
||||
};
|
||||
|
||||
@test fn diff_order_e1() void = {
|
||||
match (f_diff(1)) {
|
||||
case let v: i32 => { assert(false); };
|
||||
case e2 => { assert(false); };
|
||||
case e1 => { };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn diff_order_e2() void = {
|
||||
match (f_diff(2)) {
|
||||
case let v: i32 => { assert(false); };
|
||||
case e2 => { };
|
||||
case e1 => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn diff_order_success() void = {
|
||||
match (f_diff(0)) {
|
||||
case let v: i32 => { assert(v == 101); };
|
||||
case e2 => { assert(false); };
|
||||
case e1 => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn same_order() void = {
|
||||
match (f_same(1)) {
|
||||
case let v: i32 => { assert(false); };
|
||||
case e1 => { };
|
||||
case e2 => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn multiword() void = {
|
||||
match (f_mw(2)) {
|
||||
case let v: i32 => { assert(false); };
|
||||
case let s: emsg => {
|
||||
assert(s.len == 2);
|
||||
assert(s[0] == 65u8);
|
||||
assert(s[1] == 66u8);
|
||||
};
|
||||
case e1 => { assert(false); };
|
||||
};
|
||||
};
|
||||
38
test/lang/variant_chain_b95_runonly_test.ww
Normal file
38
test/lang/variant_chain_b95_runonly_test.ww
Normal file
@@ -0,0 +1,38 @@
|
||||
// variant_chain_b95_runonly_test — the two #95 CALL-source chain rows whose mk()
|
||||
// frame carries the pre-existing #81 uninit-local zero-fill asm divergence: the
|
||||
// `is` result is cs==ww runtime-correct, but the surrounding frame byte-diverges
|
||||
// cs≠ww (probed at HEAD: real, both standalone and @test forms). So these migrate
|
||||
// VALUE-ONLY (_runonly, byte-id-EXCLUDED) per the divergence carve; the byte-id
|
||||
// net is waived pending #81. Migrated from test/wcc/944_variant_chain_b95_run.c
|
||||
// (K_RUN_NOID rows, #5-C5).
|
||||
|
||||
package variant_chain_b95_runonly_test;
|
||||
|
||||
type cc1_base = struct { a: int, b: int };
|
||||
type cc1_al0 = cc1_base;
|
||||
|
||||
type cc2_base = struct { a: int, b: int };
|
||||
type cc2_al0 = cc2_base;
|
||||
type cc2_ali = cc2_al0;
|
||||
|
||||
fn cc1_mk() cc1_base = {
|
||||
let s: cc1_base;
|
||||
s.a = 4; s.b = 9;
|
||||
return s;
|
||||
};
|
||||
|
||||
fn cc2_mk() cc2_base = {
|
||||
let s: cc2_base;
|
||||
s.a = 4; s.b = 9;
|
||||
return s;
|
||||
};
|
||||
|
||||
@test fn chain_call_bound81() void = {
|
||||
let v: (void | cc1_al0) = cc1_mk();
|
||||
assert(v is cc1_al0);
|
||||
};
|
||||
|
||||
@test fn chain_call2_bound81() void = {
|
||||
let v: (void | cc2_ali) = cc2_mk();
|
||||
assert(v is cc2_ali);
|
||||
};
|
||||
145
test/lang/variant_chain_b95_test.ww
Normal file
145
test/lang/variant_chain_b95_test.ww
Normal file
@@ -0,0 +1,145 @@
|
||||
// variant_chain_b95_test — #95: widening a source whose TYPE reaches a union
|
||||
// variant through an alias CHAIN (or by structural fallback) must tag the right
|
||||
// variant. Pins 1/2/deep-level chain membership, exact-precedence (ali|ali2),
|
||||
// nominal (str|linerr) regression, bare-pointer source, and unrelated-struct
|
||||
// structural fallback. Migrated from test/wcc/944_variant_chain_b95_run.c
|
||||
// (K_RUN rows: build+run cs==ww + asm byte-id). The K_BUILDERR reject rows are
|
||||
// runww //ww:error carriers (test/wcc/data/vchain_*); the cstage-runs/ww-rejects
|
||||
// #277 row + the #81 call-source byte-id-waived rows are carried separately.
|
||||
// Types are per-row-prefixed because the C rows reused names with different shapes.
|
||||
|
||||
package variant_chain_b95_test;
|
||||
|
||||
type c1_base = struct { a: int, b: int };
|
||||
type c1_al0 = c1_base;
|
||||
|
||||
type c2_base = struct { a: int, b: int };
|
||||
type c2_al0 = c2_base;
|
||||
type c2_ali = c2_al0;
|
||||
|
||||
type cds_base = struct { a: int, b: int };
|
||||
type cds_ali = cds_base;
|
||||
type cds_ali2 = cds_ali;
|
||||
|
||||
type cdv_base = struct { a: int, b: int };
|
||||
type cdv_ali = cdv_base;
|
||||
type cdv_ali2 = cdv_ali;
|
||||
|
||||
type casa_base = struct { a: int, b: int };
|
||||
type casa_ali = casa_base;
|
||||
type casa_ali2 = casa_ali;
|
||||
|
||||
type casb_base = struct { a: int, b: int };
|
||||
type casb_ali = casb_base;
|
||||
type casb_ali2 = casb_ali;
|
||||
|
||||
type ns_linerr = str;
|
||||
type ne_linerr = str;
|
||||
|
||||
type ec_base = struct { a: int, b: int };
|
||||
type ec_al0 = ec_base;
|
||||
type ec_ali = ec_al0;
|
||||
|
||||
type bc_vt = struct { a: int };
|
||||
type bc_stream = *bc_vt;
|
||||
|
||||
type b2_vt = struct { a: int };
|
||||
type b2_st0 = *b2_vt;
|
||||
type b2_stream = b2_st0;
|
||||
|
||||
type us_ta = struct { a: int, b: int };
|
||||
type us_tb = struct { a: int, b: int };
|
||||
|
||||
@test fn chain_1lvl_i() void = {
|
||||
let s: c1_base;
|
||||
s.a = 4; s.b = 9;
|
||||
let v: (void | c1_al0) = s;
|
||||
assert(v is c1_al0);
|
||||
};
|
||||
|
||||
@test fn chain_2lvl_i() void = {
|
||||
let s: c2_base;
|
||||
s.a = 4; s.b = 9;
|
||||
let v: (void | c2_ali) = s;
|
||||
assert(v is c2_ali);
|
||||
};
|
||||
|
||||
@test fn chain_deep_src() void = {
|
||||
let s: cds_ali2;
|
||||
s.a = 4; s.b = 9;
|
||||
let v: (void | cds_ali) = s;
|
||||
assert(v is cds_ali);
|
||||
};
|
||||
|
||||
@test fn chain_deep_var() void = {
|
||||
let s: cdv_base;
|
||||
s.a = 4; s.b = 9;
|
||||
let v: (void | cdv_ali2) = s;
|
||||
assert(v is cdv_ali2);
|
||||
};
|
||||
|
||||
@test fn chain_amb_srcA() void = {
|
||||
let s: casa_ali;
|
||||
s.a = 4; s.b = 9;
|
||||
let v: (casa_ali | casa_ali2) = s;
|
||||
assert(v is casa_ali);
|
||||
assert(!(v is casa_ali2));
|
||||
};
|
||||
|
||||
@test fn chain_amb_srcB() void = {
|
||||
let s: casb_ali2;
|
||||
s.a = 4; s.b = 9;
|
||||
let v: (casb_ali | casb_ali2) = s;
|
||||
assert(v is casb_ali2);
|
||||
assert(!(v is casb_ali));
|
||||
};
|
||||
|
||||
@test fn nom_str() void = {
|
||||
let s: str = "ok";
|
||||
let v: (str | ns_linerr) = s;
|
||||
assert(v is str);
|
||||
assert(!(v is ns_linerr));
|
||||
};
|
||||
|
||||
@test fn nom_err() void = {
|
||||
let e: ne_linerr = "bad";
|
||||
let v: (str | ne_linerr) = e;
|
||||
assert(v is ne_linerr);
|
||||
assert(!(v is str));
|
||||
};
|
||||
|
||||
@test fn exact_ctl() void = {
|
||||
let s: ec_ali;
|
||||
s.a = 4; s.b = 9;
|
||||
let v: (void | ec_ali) = s;
|
||||
assert(v is ec_ali);
|
||||
};
|
||||
|
||||
@test fn bare_ctl() void = {
|
||||
let x: bc_vt;
|
||||
x.a = 7;
|
||||
let v: (void | bc_stream) = &x;
|
||||
assert(v is bc_stream);
|
||||
match (v) {
|
||||
case let s: bc_stream => { assert(s.a == 7); };
|
||||
case void => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn bare_2lvl() void = {
|
||||
let x: b2_vt;
|
||||
x.a = 7;
|
||||
let v: (void | b2_stream) = &x;
|
||||
assert(v is b2_stream);
|
||||
match (v) {
|
||||
case let s: b2_stream => { assert(s.a == 7); };
|
||||
case void => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn unrel_struct() void = {
|
||||
let s: us_ta;
|
||||
s.a = 4; s.b = 9;
|
||||
let v: (void | us_tb) = s;
|
||||
assert(v is us_tb);
|
||||
};
|
||||
46
test/lang/variant_typekey_test.ww
Normal file
46
test/lang/variant_typekey_test.ww
Normal file
@@ -0,0 +1,46 @@
|
||||
// variant_typekey_test — nominal-type-key variant discrimination. A call-
|
||||
// returned value with no surface name (str or i32) widened into (alias | base)
|
||||
// must tag the structural (base) variant, not the leading alias. Both the str
|
||||
// axis ((linerr|str), linerr=!str) and the scalar axis ((ec|i32), ec=!i32).
|
||||
// Migrated from test/wcc/931_variant_typekey_run.c (value rows, cs==ww).
|
||||
|
||||
package variant_typekey_test;
|
||||
|
||||
type linerr = !str;
|
||||
type ec = !i32;
|
||||
|
||||
fn getstr() str = { return "hi"; };
|
||||
fn produce_str(k: i32) (linerr | str) = {
|
||||
if (k == 0) { return getstr(); };
|
||||
let e: linerr = "boom";
|
||||
return e;
|
||||
};
|
||||
fn classify_str(k: i32) i32 = {
|
||||
match (produce_str(k)) {
|
||||
case let er: linerr => return 1;
|
||||
case let v: str => return 2;
|
||||
};
|
||||
};
|
||||
|
||||
fn getint() i32 = { return 7; };
|
||||
fn produce_int(k: i32) (ec | i32) = {
|
||||
if (k == 0) { return getint(); };
|
||||
let e: ec = 9;
|
||||
return e;
|
||||
};
|
||||
fn classify_int(k: i32) i32 = {
|
||||
match (produce_int(k)) {
|
||||
case let er: ec => return 1;
|
||||
case let v: i32 => return 2;
|
||||
};
|
||||
};
|
||||
|
||||
@test fn linerr_str_alias_first_callret() void = {
|
||||
assert(classify_str(0) == 2);
|
||||
assert(classify_str(1) == 1);
|
||||
};
|
||||
|
||||
@test fn errint_int_alias_first_callret() void = {
|
||||
assert(classify_int(0) == 2);
|
||||
assert(classify_int(1) == 1);
|
||||
};
|
||||
75
test/lang/void_error_singleton_test.ww
Normal file
75
test/lang/void_error_singleton_test.ww
Normal file
@@ -0,0 +1,75 @@
|
||||
// void_error_singleton_test — a `!void` error singleton (tag-only) across
|
||||
// return / let-init / assign / call-arg positions, plus a wide (str) success
|
||||
// payload exercising CX/R8 zeroing on the tag-only return. Migrated from
|
||||
// test/wcc/949_void_error_singleton_run.c (value rows; C driver asserted cs==ww .s).
|
||||
|
||||
package void_error_singleton_test;
|
||||
|
||||
type too_long = !void;
|
||||
|
||||
fn f_ret_singleton() (i64 | too_long) = {
|
||||
return too_long;
|
||||
};
|
||||
|
||||
fn f_ret_success() (i64 | too_long) = {
|
||||
return 42i64;
|
||||
};
|
||||
|
||||
fn use_callarg(r: (i64 | too_long)) i32 = {
|
||||
match (r) {
|
||||
case let v: i64 => return 1;
|
||||
case too_long => return 0;
|
||||
};
|
||||
return 2;
|
||||
};
|
||||
|
||||
fn f_wide(ok: bool) (str | too_long) = {
|
||||
if (ok) { return "hello"; };
|
||||
return too_long;
|
||||
};
|
||||
|
||||
@test fn return_singleton() void = {
|
||||
match (f_ret_singleton()) {
|
||||
case let v: i64 => { assert(false); };
|
||||
case too_long => { };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn return_success() void = {
|
||||
match (f_ret_success()) {
|
||||
case let v: i64 => { assert(v == 42); };
|
||||
case too_long => { assert(false); };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn letinit_singleton() void = {
|
||||
let e: (i64 | too_long) = too_long;
|
||||
match (e) {
|
||||
case let v: i64 => { assert(false); };
|
||||
case too_long => { };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn assign_singleton() void = {
|
||||
let e: (i64 | too_long) = 5i64;
|
||||
e = too_long;
|
||||
match (e) {
|
||||
case let v: i64 => { assert(false); };
|
||||
case too_long => { };
|
||||
};
|
||||
};
|
||||
|
||||
@test fn callarg_singleton() void = {
|
||||
assert(use_callarg(too_long) == 0);
|
||||
};
|
||||
|
||||
@test fn return_singleton_wide() void = {
|
||||
match (f_wide(false)) {
|
||||
case let s: str => { assert(false); };
|
||||
case too_long => { };
|
||||
};
|
||||
match (f_wide(true)) {
|
||||
case let s: str => { assert(s.len == 5); };
|
||||
case too_long => { assert(false); };
|
||||
};
|
||||
};
|
||||
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* 924_tagged_call_arg_run — wwstage caller must not drop the DX/CX/R8
|
||||
* payload words when passing a tagged-union arg sourced from a fn
|
||||
* call that returns the same tagged union (task #21).
|
||||
*
|
||||
* Pre-fix the wwstage call-arg-emit dispatch in pushargsrev only
|
||||
* recognized N_IDENT as an already-tagged source. For arg.kind ==
|
||||
* N_CALL whose callee returns the param's tagged type, the widening
|
||||
* path treated AX (the returned tag word) as a concrete-variant
|
||||
* payload, then clobbered AX with widentag for the tag-push slot.
|
||||
* The DX/CX/R8 value-words from the tagged-return ABI were silently
|
||||
* dropped — the callee read its second arg-reg (SI / DX / etc.) as
|
||||
* 0 or as the original tag.
|
||||
*
|
||||
* Cstage already had the right shape: a `type_eq(p->type, at)` guard
|
||||
* at cmd/w6c/cgen.c:4216-4221 sets widen[i]=0, then the natural-push
|
||||
* branch at 4373-4387 pushes R8/CX/DX/AX high→low. Fix aligns
|
||||
* wwstage DOWN to that shape (rule 10):
|
||||
* 1. pushargsrev's aistagged guard (cgenutil.ww) now also fires
|
||||
* when taggedcallslot(arg) == slotsize(ptype), matching cstage.
|
||||
* 2. The natural-push fallthrough adds a taggedcallslot arm that
|
||||
* pushes R8/CX/DX/AX high→low by slot size.
|
||||
* 3. cgcall's per-arg pop-count uses taggedcallslot too so the
|
||||
* next arg's POPQ doesn't land on residual tagged words.
|
||||
*
|
||||
* Rows pin:
|
||||
* - 4-variant (rune | done | more | invalid) CALL-source: the
|
||||
* original utf8 iterator shape from worker-encoding's probe.
|
||||
* Two-word tagged (16B slot), CALL→CALL composition.
|
||||
* - IDENT-source guard: a parallel test that was already correct
|
||||
* pre-fix, pinning that the IDENT fast-path stays byte-identical
|
||||
* to cstage and isn't perturbed by the fix.
|
||||
* - 2-variant `(*u8 | oserror)` sanity: narrower 16B slot variant
|
||||
* to ensure the natural-push arm doesn't regress smaller-slot
|
||||
* shapes.
|
||||
* - Multi-arg sequence: tagged-CALL arg followed by a scalar arg,
|
||||
* pinning that the pop-count fix keeps subsequent args in the
|
||||
* right register class.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "4variant_call_source_rune",
|
||||
"type done = !void; type more = !void; type invalid = !void;\n"
|
||||
"fn yield_rune() (rune | done | more | invalid) = {\n"
|
||||
" return 65u32: rune;\n"
|
||||
"};\n"
|
||||
"fn dispatch(v: (rune | done | more | invalid)) i32 = {\n"
|
||||
" match (v) {\n"
|
||||
" case let r: rune => return r: i32;\n"
|
||||
" case let d: done => return -1;\n"
|
||||
" case let m: more => return -2;\n"
|
||||
" case let e: invalid => return -3;\n"
|
||||
" };\n"
|
||||
" return -99;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (dispatch(yield_rune()) != 65) { return 11; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
{ "4variant_ident_source_rune",
|
||||
"type done = !void; type more = !void; type invalid = !void;\n"
|
||||
"fn dispatch(v: (rune | done | more | invalid)) i32 = {\n"
|
||||
" match (v) {\n"
|
||||
" case let r: rune => return r: i32;\n"
|
||||
" case let d: done => return -1;\n"
|
||||
" case let m: more => return -2;\n"
|
||||
" case let e: invalid => return -3;\n"
|
||||
" };\n"
|
||||
" return -99;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: (rune | done | more | invalid) = 65u32: rune;\n"
|
||||
" if (dispatch(v) != 65) { return 11; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
{ "2variant_call_source_ptr",
|
||||
"type oserror = !i32;\n"
|
||||
"fn yield_ptr() (*u8 | oserror) = {\n"
|
||||
" let p: *u8 = nil;\n"
|
||||
" return p;\n"
|
||||
"};\n"
|
||||
"fn dispatch(v: (*u8 | oserror)) i32 = {\n"
|
||||
" match (v) {\n"
|
||||
" case let p: *u8 => return 7;\n"
|
||||
" case let e: oserror => return e: i32;\n"
|
||||
" };\n"
|
||||
" return -99;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (dispatch(yield_ptr()) != 7) { return 11; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
{ "2variant_call_source_err",
|
||||
"type oserror = !i32;\n"
|
||||
"fn yield_err() (*u8 | oserror) = {\n"
|
||||
" let e: oserror = 42i32: oserror;\n"
|
||||
" return e;\n"
|
||||
"};\n"
|
||||
"fn dispatch(v: (*u8 | oserror)) i32 = {\n"
|
||||
" match (v) {\n"
|
||||
" case let p: *u8 => return 7;\n"
|
||||
" case let e: oserror => return e: i32;\n"
|
||||
" };\n"
|
||||
" return -99;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (dispatch(yield_err()) != 42) { return 11; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Multi-arg shape: tagged-CALL followed by a scalar i32 arg.
|
||||
* Pre-fix the pop-count would be wrong (1 word for arg0 instead
|
||||
* of 2), leaving arg1's scalar to pick up the residual tag word
|
||||
* off the stack and routing the real scalar into DX. Post-fix
|
||||
* the per-arg word count matches the push. */
|
||||
{ "tagged_call_then_scalar",
|
||||
"type oserror = !i32;\n"
|
||||
"fn yield_ptr() (*u8 | oserror) = {\n"
|
||||
" let p: *u8 = nil;\n"
|
||||
" return p;\n"
|
||||
"};\n"
|
||||
"fn use2(v: (*u8 | oserror), k: i32) i32 = {\n"
|
||||
" match (v) {\n"
|
||||
" case let p: *u8 => return k + 1;\n"
|
||||
" case let e: oserror => return k - 1;\n"
|
||||
" };\n"
|
||||
" return -99;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (use2(yield_ptr(), 41) != 42) { return 11; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
};
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/tca_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/tca_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/tca_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s",
|
||||
driver, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return got;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
char wdrv[640];
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *path; int gated_on_existence; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated_on_existence
|
||||
&& access(drivers[d].path, X_OK) != 0) {
|
||||
fprintf(stderr, "tagged_call_arg_run: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].path);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
int got = run_driver(drivers[d].path, &rows[i], i);
|
||||
total++;
|
||||
if (got != rows[i].want) {
|
||||
fprintf(stderr,
|
||||
"tagged_call_arg_run[%s][%s]: exit=%d want=%d\n",
|
||||
drivers[d].name, rows[i].label,
|
||||
got, rows[i].want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"tagged_call_arg_run: %d/%d fixtures failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("tagged_call_arg_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
/*
|
||||
* 925_nested_union_widen_run — widening a NAMED multi-variant union
|
||||
* value into an ENCLOSING union must tag the OUTER variant correctly
|
||||
* and lay the inner tagged value out as the payload at slot+8 (project
|
||||
* #218).
|
||||
*
|
||||
* Gate-blind hazard: the outer-widen of a NAMED multi-variant union
|
||||
* (`inner`) into `(size | inner)` took the tagged-SUBSET path — it
|
||||
* byte-copied the inner value at slot+0 and remapped its SUB-variants,
|
||||
* collapsing every inner sub-variant onto outer tag 0. The match
|
||||
* EXTRACT side already read the inner value from slot+8 (nested layout),
|
||||
* so store and extract disagreed and the outer arm mis-selected. The
|
||||
* byte-id (990-997) and cs==ww asm gates both passed because the
|
||||
* bootstrap never widens a multi-variant union into an enclosing one;
|
||||
* the #94 io eFinal error path (`let e: io.error = <leaf>; return e`)
|
||||
* is the first to exercise it. cstage cg_widen_tagged_store +
|
||||
* cg_variant_match (cmd/w6c/cgen.c) and the wwstage twin
|
||||
* (selfhost/cmd/wcc/cgenutil.ww cgwidentaggedstorebp + cgvariantmatch)
|
||||
* now detect the nested-variant case (the source's whole tagged type
|
||||
* matches one outer variant — recovered via a structural fallback after
|
||||
* the nominal-lossy collapse, project tinfo_lossy_nominal) and store
|
||||
* the inner value at slot+8 with the outer tag at slot+0.
|
||||
*
|
||||
* Two assertions per RUN row:
|
||||
* - RUNTIME: build with both `ww` (cstage) and `ww_ww` (wwstage),
|
||||
* run, compare the exit code. This is what was wrong pre-fix.
|
||||
* - ASM BYTE-ID: compile through `w6c` and `w6c_ww` and require
|
||||
* byte-identical .s (rule-10).
|
||||
*
|
||||
* ERR rows assert a COMPILE-ERROR on both stages (the drew collision
|
||||
* guard): when ≥2 outer variants are structurally identical, the
|
||||
* structural fallback cannot disambiguate them once nominal identity
|
||||
* is lost (#199b/B-full), so the compiler must STOP rather than silently
|
||||
* mis-tag. The over-match is unreachable under today's nominal-lossy
|
||||
* model but INVERTS when #199b lands — the guard pins the invariant now.
|
||||
*
|
||||
* Rows:
|
||||
* 1. outer_arm_select — let-widen `e: inner` into `(size | inner)`;
|
||||
* match selects the `inner` arm (the bug returned the `size` arm).
|
||||
* 2. destructure_return — `return e` widens through the tagged-return
|
||||
* ABI; the caller destructures the inner i32 payload (5) — proves
|
||||
* the payload survives the OUTER round-trip (the rule-7 gate: if
|
||||
* this fails it is the deep wrapped-slot-layout, not this fix).
|
||||
* 3. destructure_let — let-widen + nested match unwraps the inner i32
|
||||
* payload (7) — the payload survives a non-return widen too.
|
||||
* 4. single_variant — scalar widen into `(i32 | bool)`; the non-nested
|
||||
* single-variant path is unperturbed (control + byte-id witness).
|
||||
* 5. collision_guard — two structurally-identical NAMED unions as
|
||||
* sibling variants `(a | b)`; widening must COMPILE-ERROR (the
|
||||
* guard), not silently pick one.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* experr != 0: the source must FAIL to compile on both stages. */
|
||||
struct row { const char *label; const char *src; int want; int experr; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "outer_arm_select",
|
||||
"type inner = !(i32|bool);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e: inner = (true: inner);\n"
|
||||
" let r: (size | inner) = e;\n"
|
||||
" match (r) {\n"
|
||||
" case let n: size => return 50;\n"
|
||||
" case let x: inner => return 0;\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0, 0 },
|
||||
{ "destructure_return",
|
||||
"type inner = !(i32|bool);\n"
|
||||
"fn g() (size | inner) = {\n"
|
||||
" let e: inner = (5i32: inner);\n"
|
||||
" return e;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" match (g()) {\n"
|
||||
" case let s: size => return 90;\n"
|
||||
" case let x: inner => match (x) {\n"
|
||||
" case let i: i32 => return i;\n"
|
||||
" case let b: bool => return 99;\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
5, 0 },
|
||||
{ "destructure_let",
|
||||
"type inner = !(i32|bool);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e: inner = (7i32: inner);\n"
|
||||
" let r: (size | inner) = e;\n"
|
||||
" match (r) {\n"
|
||||
" case let n: size => return 50;\n"
|
||||
" case let x: inner => match (x) {\n"
|
||||
" case let i: i32 => return i;\n"
|
||||
" case let b: bool => return 88;\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
7, 0 },
|
||||
{ "single_variant",
|
||||
"export fn main() i32 = {\n"
|
||||
" let r: (i32 | bool) = 7i32;\n"
|
||||
" match (r) {\n"
|
||||
" case let n: i32 => return n;\n"
|
||||
" case let b: bool => return 88;\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
7, 0 },
|
||||
{ "collision_guard",
|
||||
"type a = !(i32|bool);\n"
|
||||
"type b = !(i32|bool);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: a = (1i32: a);\n"
|
||||
" let r: (a | b) = x;\n"
|
||||
" match (r) {\n"
|
||||
" case let p: a => return 1;\n"
|
||||
" case let q: b => return 2;\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0, 1 },
|
||||
};
|
||||
|
||||
/* Write r->src to <dir>/<base>.ww; returns 0 on success. */
|
||||
static int
|
||||
write_src(const char *dir, const char *base, const struct row *r, char *out,
|
||||
size_t outsz)
|
||||
{
|
||||
snprintf(out, outsz, "%s/%s.ww", dir, base);
|
||||
FILE *f = fopen(out, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Build src with `driver build` inside its own subdir, run the binary,
|
||||
* return its exit code (or -1 on a build/exec failure). */
|
||||
static int
|
||||
build_run(const char *driver, const char *src, const char *workdir)
|
||||
{
|
||||
char cmd[8192];
|
||||
snprintf(cmd, sizeof cmd, "cd %s && %s build %s > /dev/null 2>&1",
|
||||
workdir, driver, src);
|
||||
if (runwait(cmd) != 0) return -1;
|
||||
|
||||
const char *base = strrchr(src, '/');
|
||||
base = base ? base + 1 : src;
|
||||
char outbin[1024];
|
||||
snprintf(outbin, sizeof outbin, "%s/%s", workdir, base);
|
||||
char *dot = strrchr(outbin, '.');
|
||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||
return runwait(outbin);
|
||||
}
|
||||
|
||||
static int
|
||||
files_equal(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int ca, cb, eq = 1;
|
||||
do {
|
||||
ca = fgetc(fa);
|
||||
cb = fgetc(fb);
|
||||
if (ca != cb) { eq = 0; break; }
|
||||
} while (ca != EOF);
|
||||
fclose(fa);
|
||||
fclose(fb);
|
||||
return eq;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[640], wdrv[640], cw6[640], ww6[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
snprintf(cw6, sizeof cw6, "%s/w6c", bin);
|
||||
snprintf(ww6, sizeof ww6, "%s/w6c_ww", bin);
|
||||
|
||||
int have_ww = (access(wdrv, X_OK) == 0);
|
||||
int have_w6cww = (access(ww6, X_OK) == 0);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
const struct row *r = &rows[i];
|
||||
|
||||
char dir[] = "/tmp/nuw.XXXXXX";
|
||||
if (mkdtemp(dir) == NULL) {
|
||||
fprintf(stderr, "row[%s]: mkdtemp failed\n", r->label);
|
||||
fail++; total++;
|
||||
continue;
|
||||
}
|
||||
|
||||
char src[1024];
|
||||
if (write_src(dir, "p", r, src, sizeof src) != 0) {
|
||||
fprintf(stderr, "row[%s]: write src failed\n", r->label);
|
||||
fail++; total++;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (r->experr) {
|
||||
/* Collision guard: w6c (and w6c_ww) must REFUSE to
|
||||
* emit. A successful emit is the silent mis-tag the
|
||||
* guard exists to stop. */
|
||||
char css[1024], wss[1024], cmd[8192];
|
||||
snprintf(css, sizeof css, "%s/cs.s", dir);
|
||||
snprintf(wss, sizeof wss, "%s/ww.s", dir);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
|
||||
cw6, css, src);
|
||||
total++;
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s][cstage]: emit SUCCEEDED, "
|
||||
"want compile-error (#218/#199b)\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
if (have_w6cww) {
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"%s -o %s %s > /dev/null 2>&1",
|
||||
ww6, wss, src);
|
||||
total++;
|
||||
if (runwait(cmd) == 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s][wwstage]: emit SUCCEEDED, "
|
||||
"want compile-error (#218/#199b)\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* ASM byte-id: w6c vs w6c_ww .s must be identical. */
|
||||
if (have_w6cww) {
|
||||
char css[1024], wss[1024], cmd[8192];
|
||||
snprintf(css, sizeof css, "%s/cs.s", dir);
|
||||
snprintf(wss, sizeof wss, "%s/ww.s", dir);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
|
||||
cw6, css, src);
|
||||
int rc1 = runwait(cmd);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
|
||||
ww6, wss, src);
|
||||
int rc2 = runwait(cmd);
|
||||
total++;
|
||||
if (rc1 != 0 || rc2 != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: w6c/w6c_ww emit failed (%d/%d)\n",
|
||||
r->label, rc1, rc2);
|
||||
fail++;
|
||||
} else if (files_equal(css, wss) != 1) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cs.s != ww.s (rule-10 break)\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
/* RUNTIME: cstage. */
|
||||
{
|
||||
char wk[1024];
|
||||
snprintf(wk, sizeof wk, "%s/cs", dir);
|
||||
mkdir(wk, 0755);
|
||||
int got = build_run(cdrv, src, wk);
|
||||
total++;
|
||||
if (got != r->want) {
|
||||
fprintf(stderr,
|
||||
"row[%s][cstage]: exit=%d want=%d\n",
|
||||
r->label, got, r->want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
/* RUNTIME: wwstage (the second half of rule-10). */
|
||||
if (have_ww) {
|
||||
char wk[1024];
|
||||
snprintf(wk, sizeof wk, "%s/ww", dir);
|
||||
mkdir(wk, 0755);
|
||||
int got = build_run(wdrv, src, wk);
|
||||
total++;
|
||||
if (got != r->want) {
|
||||
fprintf(stderr,
|
||||
"row[%s][wwstage]: exit=%d want=%d\n",
|
||||
r->label, got, r->want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
{
|
||||
char rm[1100];
|
||||
snprintf(rm, sizeof rm, "rm -rf %s", dir);
|
||||
runwait(rm);
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"nested_union_widen_run: %d/%d checks failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("nested_union_widen_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,330 +0,0 @@
|
||||
/*
|
||||
* 925_tryprop_tag_remap_run — the `?` try operator must remap the
|
||||
* operand union's error-variant tag into the ENCLOSING fn return
|
||||
* union's variant order before propagating (project #173).
|
||||
*
|
||||
* Gate-blind hazard: when the `?` operand union and the enclosing fn
|
||||
* return union differ in variant ORDER, the raw operand tag names the
|
||||
* WRONG variant in the return union. Pre-fix wwstage cgtryprop did NO
|
||||
* remap (selfhost/cmd/wcc/cgenexpr.ww) and propagated the raw tag, so
|
||||
* the caller saw the wrong error variant at runtime — while the
|
||||
* byte-id (990-997) and cs==ww asm gates both passed, because the
|
||||
* bootstrap only ever tries SAME-order unions. cstage already remapped
|
||||
* (cmd/w6c/cgen.c:6161-6184); the fix ports that loop into wwstage.
|
||||
*
|
||||
* Two assertions per row:
|
||||
* - RUNTIME: build with both `ww` (cstage) and `ww_ww` (wwstage),
|
||||
* run, compare the exit code (= which variant the caller matched).
|
||||
* This is what was wrong pre-fix.
|
||||
* - ASM BYTE-ID: compile the same source through `w6c` and `w6c_ww`
|
||||
* and require byte-identical .s. Proves rule-10 (symmetric stages)
|
||||
* holds AFTER the fix on differing-order too, and that same-order
|
||||
* emits no remap (zero delta).
|
||||
*
|
||||
* Rows:
|
||||
* 1. diff_order_e1 — g→e1 on a branched callee; f's union swaps the
|
||||
* error order; caller asserts the e1 arm. Pre-fix wwstage = 2.
|
||||
* 2. diff_order_e2 — g→e2; caller asserts the e2 arm. Proves BOTH
|
||||
* error variants remap (not just the first).
|
||||
* 3. diff_order_success — g→value; caller asserts the i32 success
|
||||
* unwrap survives unperturbed by the remap.
|
||||
* 4. same_order — f's union same order as g; the only shape the
|
||||
* bootstrap exercises; byte-id witness that the remap is inert.
|
||||
* 5. multiword — differing-order propagation of a MULTI-WORD `!str`
|
||||
* error (payload in DX/CX/R8). Caller asserts the PAYLOAD BYTES
|
||||
* ("AB") survive, not just the tag — proves the remap touches
|
||||
* only AX and the payload regs ride the RET.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "diff_order_e1",
|
||||
"type e1 = !void;\n"
|
||||
"type e2 = !void;\n"
|
||||
"fn g(which: i32) (i32 | e1 | e2) = {\n"
|
||||
" if (which == 1) { return e1{}; };\n"
|
||||
" if (which == 2) { return e2{}; };\n"
|
||||
" return 100;\n"
|
||||
"};\n"
|
||||
"fn f(which: i32) (i32 | e2 | e1) = {\n"
|
||||
" let v: i32 = g(which)?;\n"
|
||||
" return v + 1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" match (f(1)) {\n"
|
||||
" case let v: i32 => return 50;\n"
|
||||
" case e2 => return 2;\n"
|
||||
" case e1 => return 1;\n"
|
||||
" };\n"
|
||||
" return 99;\n"
|
||||
"};\n",
|
||||
1 },
|
||||
{ "diff_order_e2",
|
||||
"type e1 = !void;\n"
|
||||
"type e2 = !void;\n"
|
||||
"fn g(which: i32) (i32 | e1 | e2) = {\n"
|
||||
" if (which == 1) { return e1{}; };\n"
|
||||
" if (which == 2) { return e2{}; };\n"
|
||||
" return 100;\n"
|
||||
"};\n"
|
||||
"fn f(which: i32) (i32 | e2 | e1) = {\n"
|
||||
" let v: i32 = g(which)?;\n"
|
||||
" return v + 1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" match (f(2)) {\n"
|
||||
" case let v: i32 => return 50;\n"
|
||||
" case e2 => return 2;\n"
|
||||
" case e1 => return 1;\n"
|
||||
" };\n"
|
||||
" return 99;\n"
|
||||
"};\n",
|
||||
2 },
|
||||
{ "diff_order_success",
|
||||
"type e1 = !void;\n"
|
||||
"type e2 = !void;\n"
|
||||
"fn g(which: i32) (i32 | e1 | e2) = {\n"
|
||||
" if (which == 1) { return e1{}; };\n"
|
||||
" if (which == 2) { return e2{}; };\n"
|
||||
" return 100;\n"
|
||||
"};\n"
|
||||
"fn f(which: i32) (i32 | e2 | e1) = {\n"
|
||||
" let v: i32 = g(which)?;\n"
|
||||
" return v + 1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" match (f(0)) {\n"
|
||||
" case let v: i32 => { if (v != 101) { return 40; }; return 7; };\n"
|
||||
" case e2 => return 2;\n"
|
||||
" case e1 => return 1;\n"
|
||||
" };\n"
|
||||
" return 99;\n"
|
||||
"};\n",
|
||||
7 },
|
||||
{ "same_order",
|
||||
"type e1 = !void;\n"
|
||||
"type e2 = !void;\n"
|
||||
"fn g(which: i32) (i32 | e1 | e2) = {\n"
|
||||
" if (which == 1) { return e1{}; };\n"
|
||||
" if (which == 2) { return e2{}; };\n"
|
||||
" return 100;\n"
|
||||
"};\n"
|
||||
"fn f(which: i32) (i32 | e1 | e2) = {\n"
|
||||
" let v: i32 = g(which)?;\n"
|
||||
" return v + 1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" match (f(1)) {\n"
|
||||
" case let v: i32 => return 50;\n"
|
||||
" case e1 => return 1;\n"
|
||||
" case e2 => return 2;\n"
|
||||
" };\n"
|
||||
" return 99;\n"
|
||||
"};\n",
|
||||
1 },
|
||||
{ "multiword",
|
||||
"type e1 = !void;\n"
|
||||
"type emsg = !str;\n"
|
||||
"fn g(which: i32) (i32 | e1 | emsg) = {\n"
|
||||
" if (which == 1) { return e1{}; };\n"
|
||||
" if (which == 2) { let m: emsg = \"AB\": emsg; return m; };\n"
|
||||
" return 100;\n"
|
||||
"};\n"
|
||||
"fn f(which: i32) (i32 | emsg | e1) = {\n"
|
||||
" let v: i32 = g(which)?;\n"
|
||||
" return v + 1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" match (f(2)) {\n"
|
||||
" case let v: i32 => return 50;\n"
|
||||
" case let s: emsg => {\n"
|
||||
" if (s.len != 2) { return 30; };\n"
|
||||
" if (s[0] != 65u8) { return 31; };\n"
|
||||
" if (s[1] != 66u8) { return 32; };\n"
|
||||
" return 7;\n"
|
||||
" };\n"
|
||||
" case e1 => return 1;\n"
|
||||
" };\n"
|
||||
" return 99;\n"
|
||||
"};\n",
|
||||
7 },
|
||||
};
|
||||
|
||||
/* Write r->src to <dir>/<base>.ww; returns 0 on success. */
|
||||
static int
|
||||
write_src(const char *dir, const char *base, const struct row *r, char *out,
|
||||
size_t outsz)
|
||||
{
|
||||
snprintf(out, outsz, "%s/%s.ww", dir, base);
|
||||
FILE *f = fopen(out, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Build src with `driver build` inside its own subdir, run the binary,
|
||||
* return its exit code (or -1 on a build/exec failure). */
|
||||
static int
|
||||
build_run(const char *driver, const char *src, const char *workdir)
|
||||
{
|
||||
char cmd[8192];
|
||||
snprintf(cmd, sizeof cmd, "cd %s && %s build %s > /dev/null 2>&1",
|
||||
workdir, driver, src);
|
||||
if (runwait(cmd) != 0) return -1;
|
||||
|
||||
const char *base = strrchr(src, '/');
|
||||
base = base ? base + 1 : src;
|
||||
char outbin[1024];
|
||||
snprintf(outbin, sizeof outbin, "%s/%s", workdir, base);
|
||||
char *dot = strrchr(outbin, '.');
|
||||
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
|
||||
return runwait(outbin);
|
||||
}
|
||||
|
||||
static int
|
||||
files_equal(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int ca, cb, eq = 1;
|
||||
do {
|
||||
ca = fgetc(fa);
|
||||
cb = fgetc(fb);
|
||||
if (ca != cb) { eq = 0; break; }
|
||||
} while (ca != EOF);
|
||||
fclose(fa);
|
||||
fclose(fb);
|
||||
return eq;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[640], wdrv[640], cw6[640], ww6[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
snprintf(cw6, sizeof cw6, "%s/w6c", bin);
|
||||
snprintf(ww6, sizeof ww6, "%s/w6c_ww", bin);
|
||||
|
||||
int have_ww = (access(wdrv, X_OK) == 0);
|
||||
int have_w6cww = (access(ww6, X_OK) == 0);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
const struct row *r = &rows[i];
|
||||
|
||||
char dir[] = "/tmp/tprm.XXXXXX";
|
||||
if (mkdtemp(dir) == NULL) {
|
||||
fprintf(stderr, "row[%s]: mkdtemp failed\n", r->label);
|
||||
fail++; total++;
|
||||
continue;
|
||||
}
|
||||
|
||||
char src[1024];
|
||||
if (write_src(dir, "p", r, src, sizeof src) != 0) {
|
||||
fprintf(stderr, "row[%s]: write src failed\n", r->label);
|
||||
fail++; total++;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* ASM byte-id: w6c vs w6c_ww .s must be identical. */
|
||||
if (have_w6cww) {
|
||||
char css[1024], wss[1024], cmd[8192];
|
||||
snprintf(css, sizeof css, "%s/cs.s", dir);
|
||||
snprintf(wss, sizeof wss, "%s/ww.s", dir);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
|
||||
cw6, css, src);
|
||||
int rc1 = runwait(cmd);
|
||||
snprintf(cmd, sizeof cmd, "%s -o %s %s > /dev/null 2>&1",
|
||||
ww6, wss, src);
|
||||
int rc2 = runwait(cmd);
|
||||
total++;
|
||||
if (rc1 != 0 || rc2 != 0) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: w6c/w6c_ww emit failed (%d/%d)\n",
|
||||
r->label, rc1, rc2);
|
||||
fail++;
|
||||
} else if (files_equal(css, wss) != 1) {
|
||||
fprintf(stderr,
|
||||
"row[%s]: cs.s != ww.s (rule-10 break)\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
/* RUNTIME: cstage. */
|
||||
{
|
||||
char wk[1024];
|
||||
snprintf(wk, sizeof wk, "%s/cs", dir);
|
||||
mkdir(wk, 0755);
|
||||
int got = build_run(cdrv, src, wk);
|
||||
total++;
|
||||
if (got != r->want) {
|
||||
fprintf(stderr,
|
||||
"row[%s][cstage]: exit=%d want=%d\n",
|
||||
r->label, got, r->want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
/* RUNTIME: wwstage (the side #173 fixes). */
|
||||
if (have_ww) {
|
||||
char wk[1024];
|
||||
snprintf(wk, sizeof wk, "%s/ww", dir);
|
||||
mkdir(wk, 0755);
|
||||
int got = build_run(wdrv, src, wk);
|
||||
total++;
|
||||
if (got != r->want) {
|
||||
fprintf(stderr,
|
||||
"row[%s][wwstage]: exit=%d want=%d\n",
|
||||
r->label, got, r->want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
cleanup:
|
||||
{
|
||||
char rm[1100];
|
||||
snprintf(rm, sizeof rm, "rm -rf %s", dir);
|
||||
runwait(rm);
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"tryprop_tag_remap_run: %d/%d checks failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("tryprop_tag_remap_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* 926_match_slice_variant_run — Class B semantic test for task #19.
|
||||
* Pre-fix wwstage emitted internally-consistent but wrong tag values
|
||||
* for any `(scalar | []T)` widen/dispatch: every []T slot collapsed
|
||||
* onto tag 0, so the match arm that fired was always the leading
|
||||
* variant regardless of which arm the caller meant. cstage was
|
||||
* correct on these shapes.
|
||||
*
|
||||
* 722 pins asm byte-id (the polarity sentinel). This file pins the
|
||||
* end-to-end runtime: build through each driver and confirm both
|
||||
* arms reachable, with payload survival on the slice arm.
|
||||
*
|
||||
* Matrix per rob (task #19): (u8 | []u8), reverse-order ([]u8 | u8),
|
||||
* other scalar-vs-slice-of-same-primitive at i8/i32/u64/rune widths,
|
||||
* and a three-arm (u8 | []u8 | str).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* The canonical bytes.index shape: scalar arm returns 1, slice
|
||||
* arm returns 2, and the slice arm also checks payload survives
|
||||
* (sub.len + first byte) so a silent payload-drop isn't masked. */
|
||||
{ "u8_slice_u8",
|
||||
"fn pick(n: (u8 | []u8)) i32 = {\n"
|
||||
" match (n) {\n"
|
||||
" case let c: u8 => return c: i32;\n"
|
||||
" case let s: []u8 => return 100i32 + (s.len: i32) + (s[0]: i32);\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [3]u8;\n"
|
||||
" buf[0] = 7u8; buf[1] = 8u8; buf[2] = 9u8;\n"
|
||||
" if (pick(42u8) != 42) { return 11; };\n"
|
||||
" if (pick(buf[0:3]) != 110) { return 12; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Reverse-order: same shape with slice arm at idx 0. The
|
||||
* shape-fallback in taggedvariantindex must yield idx 0 here,
|
||||
* idx 1 for the prior row — the bug's asymmetry catch. */
|
||||
{ "slice_u8_then_u8",
|
||||
"fn pick(n: ([]u8 | u8)) i32 = {\n"
|
||||
" match (n) {\n"
|
||||
" case let s: []u8 => return 100i32 + (s.len: i32) + (s[0]: i32);\n"
|
||||
" case let c: u8 => return c: i32;\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [3]u8;\n"
|
||||
" buf[0] = 7u8; buf[1] = 8u8; buf[2] = 9u8;\n"
|
||||
" if (pick(buf[0:3]) != 110) { return 11; };\n"
|
||||
" if (pick(42u8) != 42) { return 12; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* (i8 | []i8) — other signed-narrow primitive. */
|
||||
{ "i8_slice_i8",
|
||||
"fn pick(n: (i8 | []i8)) i32 = {\n"
|
||||
" match (n) {\n"
|
||||
" case let c: i8 => return c: i32;\n"
|
||||
" case let s: []i8 => return 100i32 + (s.len: i32) + (s[0]: i32);\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [2]i8;\n"
|
||||
" buf[0] = 5i8; buf[1] = 6i8;\n"
|
||||
" if (pick(11i8) != 11) { return 11; };\n"
|
||||
" if (pick(buf[0:2]) != 107) { return 12; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* (i32 | []i32) — wider scalar payload arm. */
|
||||
{ "i32_slice_i32",
|
||||
"fn pick(n: (i32 | []i32)) i32 = {\n"
|
||||
" match (n) {\n"
|
||||
" case let c: i32 => return c;\n"
|
||||
" case let s: []i32 => return 1000i32 + (s.len: i32) + s[0];\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [2]i32;\n"
|
||||
" buf[0] = 33i32; buf[1] = 44i32;\n"
|
||||
" if (pick(77i32) != 77) { return 11; };\n"
|
||||
" if (pick(buf[0:2]) != 1035) { return 12; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* (u64 | []u64) — 8-byte scalar arm; the scalar tag at idx 0
|
||||
* and the slice payload words must not collide. */
|
||||
{ "u64_slice_u64",
|
||||
"fn pick(n: (u64 | []u64)) i32 = {\n"
|
||||
" match (n) {\n"
|
||||
" case let c: u64 => return c: i32;\n"
|
||||
" case let s: []u64 => return 1000i32 + (s.len: i32) + (s[0]: i32);\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [2]u64;\n"
|
||||
" buf[0] = 41u64; buf[1] = 42u64;\n"
|
||||
" if (pick(99u64) != 99) { return 11; };\n"
|
||||
" if (pick(buf[0:2]) != 1043) { return 12; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* (rune | []rune) — Hare's iterator-pair shape (a rune or a
|
||||
* slice of runes). rune is u32-wide, []rune is 24B. */
|
||||
{ "rune_slice_rune",
|
||||
"fn pick(n: (rune | []rune)) i32 = {\n"
|
||||
" match (n) {\n"
|
||||
" case let r: rune => return r: i32;\n"
|
||||
" case let s: []rune => return 1000i32 + (s.len: i32) + (s[0]: i32);\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [2]rune;\n"
|
||||
" buf[0] = 'a'; buf[1] = 'b';\n"
|
||||
" if (pick('Z') != 90) { return 11; };\n"
|
||||
" if (pick(buf[0:2]) != 1099) { return 12; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Three-arm (u8 | []u8 | str): scalar at idx 0, slice at idx 1,
|
||||
* str at idx 2. Verifies the slice axis composes with the
|
||||
* pre-existing str shape pass — no regression on str arm. */
|
||||
{ "u8_slice_u8_str",
|
||||
"fn pick(n: (u8 | []u8 | str)) i32 = {\n"
|
||||
" match (n) {\n"
|
||||
" case let c: u8 => return c: i32;\n"
|
||||
" case let s: []u8 => return 200i32 + (s.len: i32) + (s[0]: i32);\n"
|
||||
" case let t: str => return 300i32 + (t.len: i32);\n"
|
||||
" };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let buf: [3]u8;\n"
|
||||
" buf[0] = 1u8; buf[1] = 2u8; buf[2] = 3u8;\n"
|
||||
// Three-arm 32B-slot widening through chained if-call sites
|
||||
// trips a separate sequential-push bug in both stages; bind
|
||||
// each call to a let before checking to keep the row focused
|
||||
// on variant-tag selection.
|
||||
" let a: i32 = pick(5u8);\n"
|
||||
" let b: i32 = pick(buf[0:3]);\n"
|
||||
" let c: i32 = pick(\"hi\");\n"
|
||||
" if (a != 5) { return 11; };\n"
|
||||
" if (b != 204) { return 12; };\n"
|
||||
" if (c != 302) { return 13; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
};
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char src[128], tmpdir[96], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/msv_run_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/msv_run_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/msv_run_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s", driver, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return got;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
char wdrv[640];
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *path; int gated_on_existence; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated_on_existence
|
||||
&& access(drivers[d].path, X_OK) != 0) {
|
||||
fprintf(stderr,
|
||||
"match_slice_variant_run: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].path);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
int got = run_driver(drivers[d].path, &rows[i], i);
|
||||
total++;
|
||||
if (got != rows[i].want) {
|
||||
fprintf(stderr,
|
||||
"match_slice_variant_run[%s][%s]: exit=%d want=%d\n",
|
||||
drivers[d].name, rows[i].label,
|
||||
got, rows[i].want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"match_slice_variant_run: %d/%d fixtures failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("match_slice_variant_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
/*
|
||||
* 927_is_nonident_run — `e is T` on a NON-IDENT scrutinee (#45).
|
||||
*
|
||||
* Pre-#45 wwstage cgtypetest resolved ONLY lhs.kind == N_IDENT; every
|
||||
* other scrutinee shape (xs[i], p.field, call()) fell through with
|
||||
* scrutoff = 0 + scrutt = nil, emitting `MOVQ (BP), AX; CMPQ $0, AX`
|
||||
* — the tag read landed on the saved-BP word and the variant index
|
||||
* clamped to 0. SILENT cs≠ww miscompile (cstage N_TYPETEST cgexprs
|
||||
* the scrutinee and compares the real tag in AX); the regex fold-2a
|
||||
* epilogue guard `insts[insts.len-1] is inst_match` is the consumer
|
||||
* that surfaced it. The `as` twin already had the non-ident
|
||||
* @asrt_spill fallback (#200); `is` mirrors cstage's no-spill shape
|
||||
* instead (only the tag word is consumed).
|
||||
*
|
||||
* Rows pin every scrutinee shape: indexed (constant and len-1 index),
|
||||
* dot-field, call-result, the `is []T` slice-variant axis on an
|
||||
* indexed scrutinee, the nullable `(*T | void)` pointer-vs-null
|
||||
* discriminator on both ident and non-ident scrutinees (review
|
||||
* finding: the first non-ident arm tag-compared the pointer; the
|
||||
* ident half was missing the nullable arm since before #45), and the
|
||||
* ident control (asm hand-checked unchanged vs the pre-#45 compiler
|
||||
* when the fix landed). Per row:
|
||||
* w6c vs w6c_ww byte-id (rule 10 — cstage is the runtime-correct
|
||||
* reference shape) + runtime via both the ww and ww_ww drivers.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want;
|
||||
};
|
||||
|
||||
#define P48_TYPES \
|
||||
"type p48 = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64 };\n" \
|
||||
"type small = (p48 | bool);\n"
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* The discovering repro: indexed scrutinee, constant and
|
||||
* xs.len-1 index, asserted both polarities (a bool-built
|
||||
* element must answer `is bool` true and `is p48` false —
|
||||
* pre-#45 wwstage answered `is p48` TRUE, exit 2). */
|
||||
{ "indexed",
|
||||
P48_TYPES
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []small;\n"
|
||||
" let b: bool = true;\n"
|
||||
" let v: small = b;\n"
|
||||
" append(xs, v);\n"
|
||||
" if (xs.len == 0 || !(xs[xs.len - 1] is bool)) {\n"
|
||||
" return 1;\n"
|
||||
" };\n"
|
||||
" if (xs[xs.len - 1] is p48) {\n"
|
||||
" return 2;\n"
|
||||
" };\n"
|
||||
" if (!(xs[0] is bool)) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Dot-field scrutinee. */
|
||||
{ "dot_field",
|
||||
P48_TYPES
|
||||
"type holder = struct { v: small, k: i64 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let b: bool = true;\n"
|
||||
" let h: holder = holder { v = b, k = 5 };\n"
|
||||
" if (!(h.v is bool)) { return 1; };\n"
|
||||
" if (h.v is p48) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Call-result scrutinee (register-class tagged return; the
|
||||
* sret-class call stays #38b loud-stopped). */
|
||||
{ "call_result",
|
||||
"type val = (i64 | bool);\n"
|
||||
"fn mk(flag: bool) val = {\n"
|
||||
" if (flag) {\n"
|
||||
" let b: bool = true;\n"
|
||||
" return b;\n"
|
||||
" };\n"
|
||||
" let n: i64 = 7;\n"
|
||||
" return n;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (!(mk(true) is bool)) { return 1; };\n"
|
||||
" if (!(mk(false) is i64)) { return 2; };\n"
|
||||
" if (mk(true) is i64) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* `is []T` on an indexed scrutinee — the flatslicevariantidx
|
||||
* axis of the non-ident variant resolution. */
|
||||
{ "slice_variant_indexed",
|
||||
"type val = (i64 | []u8);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: []val;\n"
|
||||
" let buf: [2]u8 = [1u8, 2u8];\n"
|
||||
" let s: []u8 = buf[0:2];\n"
|
||||
" let v: val = s;\n"
|
||||
" append(xs, v);\n"
|
||||
" let n: i64 = 5;\n"
|
||||
" let w: val = n;\n"
|
||||
" append(xs, w);\n"
|
||||
" if (!(xs[0] is []u8)) { return 1; };\n"
|
||||
" if (!(xs[1] is i64)) { return 2; };\n"
|
||||
" if (xs[0] is i64) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Nullable `(*T | void)` fold on a NON-IDENT scrutinee: the
|
||||
* discriminator is pointer-vs-null, not tag-vs-index (cstage
|
||||
* N_TYPETEST nullable arm emits CMPQ $0 + JE/JNE polarity).
|
||||
* Surfaced in #45 review: the first non-ident arm compared the
|
||||
* pointer against the variant index — `h.m is *t` on a non-null
|
||||
* pointer answered FALSE (silent cs≠ww, wwstage exit 1). Both
|
||||
* polarities, both states (ptr and void). */
|
||||
{ "nullable_dot_field",
|
||||
"type t = struct { a: i64 };\n"
|
||||
"type maybe = (*t | void);\n"
|
||||
"type holder = struct { m: maybe, k: i64 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: t = t { a = 5 };\n"
|
||||
" let h: holder = holder { m = &v, k = 1 };\n"
|
||||
" if (!(h.m is *t)) { return 1; };\n"
|
||||
" if (h.m is void) { return 2; };\n"
|
||||
" let h2: holder = holder { m = void, k = 2 };\n"
|
||||
" if (h2.m is *t) { return 3; };\n"
|
||||
" if (!(h2.m is void)) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Nullable IDENT scrutinee: the same missing nullable arm hit
|
||||
* the ident path too (pre-existing at master, closed by the same
|
||||
* shared compare choke-point). */
|
||||
{ "nullable_ident",
|
||||
"type t = struct { a: i64 };\n"
|
||||
"type maybe = (*t | void);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: t = t { a = 5 };\n"
|
||||
" let m: maybe = &v;\n"
|
||||
" if (!(m is *t)) { return 1; };\n"
|
||||
" if (m is void) { return 2; };\n"
|
||||
" let m2: maybe = void;\n"
|
||||
" if (m2 is *t) { return 3; };\n"
|
||||
" if (!(m2 is void)) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Ident control: the pre-existing slot-load path must be
|
||||
* untouched (also hand-cmp'd vs the pre-#45 w6c_ww). */
|
||||
{ "ident_control",
|
||||
"type val = (i64 | bool);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let b: bool = true;\n"
|
||||
" let v: val = b;\n"
|
||||
" if (!(v is bool)) { return 1; };\n"
|
||||
" if (v is i64) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
};
|
||||
|
||||
static const char *g_bin;
|
||||
|
||||
static int
|
||||
compile_s(const char *tool, const char *src, const char *outpath)
|
||||
{
|
||||
char cmd[1024];
|
||||
snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2>&1",
|
||||
g_bin, tool, src, outpath);
|
||||
return runwait(cmd);
|
||||
}
|
||||
|
||||
static int
|
||||
file_eq(const char *a, const char *b)
|
||||
{
|
||||
char cmd[1024];
|
||||
snprintf(cmd, sizeof cmd, "cmp -s %s %s", a, b);
|
||||
return runwait(cmd) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const char *src, const char *label)
|
||||
{
|
||||
char tmpdir[128], outbin[160], rmcmd[200], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/isnonid_%d_d", getpid());
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(outbin, sizeof outbin, "%s/m", tmpdir);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1",
|
||||
g_bin, driver, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
int got = runwait(outbin);
|
||||
runwait(rmcmd);
|
||||
return got;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
static char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
g_bin = bin;
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
const struct row *r = &rows[i];
|
||||
char src[128], cs_s[128], ww_s[128];
|
||||
snprintf(src, sizeof src, "/tmp/isnonid_%d_%d.ww",
|
||||
getpid(), i);
|
||||
snprintf(cs_s, sizeof cs_s, "/tmp/isnonid_%d_%d_cs.s",
|
||||
getpid(), i);
|
||||
snprintf(ww_s, sizeof ww_s, "/tmp/isnonid_%d_%d_ww.s",
|
||||
getpid(), i);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return 1;
|
||||
fputs("package main;\n\n", f);
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
total++;
|
||||
int cs_rc = compile_s("w6c", src, cs_s);
|
||||
int ww_rc = compile_s("w6c_ww", src, ww_s);
|
||||
if (cs_rc != 0 || ww_rc != 0) {
|
||||
fprintf(stderr, "FAIL row[%s]: compile rc cs=%d "
|
||||
"ww=%d\n", r->label, cs_rc, ww_rc);
|
||||
fail++;
|
||||
unlink(src); unlink(cs_s); unlink(ww_s);
|
||||
continue;
|
||||
}
|
||||
if (!file_eq(cs_s, ww_s)) {
|
||||
fprintf(stderr, "FAIL row[%s]: cs != ww .s\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
int got_cs = run_driver("ww", src, r->label);
|
||||
if (got_cs != r->want) {
|
||||
fprintf(stderr, "FAIL row[%s] cstage: want %d "
|
||||
"got %d\n", r->label, r->want, got_cs);
|
||||
fail++;
|
||||
}
|
||||
char wwdrv[600];
|
||||
snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin);
|
||||
if (access(wwdrv, X_OK) == 0) {
|
||||
int got_ww = run_driver("ww_ww", src, r->label);
|
||||
if (got_ww != r->want) {
|
||||
fprintf(stderr, "FAIL row[%s] wwstage: "
|
||||
"want %d got %d\n",
|
||||
r->label, r->want, got_ww);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
unlink(src); unlink(cs_s); unlink(ww_s);
|
||||
}
|
||||
if (fail) {
|
||||
fprintf(stderr, "is_nonident_run: %d/%d rows failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("is_nonident_run: %d rows ok\n", total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,290 +0,0 @@
|
||||
/*
|
||||
* 928_match_nonident_idx_run — `match` on an N_INDEX scrutinee whose
|
||||
* BASE is not an ident (#48).
|
||||
*
|
||||
* Pre-#48 wwstage matchscrutt's N_INDEX arm required ibase.kind ==
|
||||
* N_IDENT; an index over a struct field (`match (h.xs[i])` = N_INDEX
|
||||
* over N_DOT) returned nil → cgmatch dispatched with scrutt = nil:
|
||||
* every case arm's variant index clamped to 0 (CMPQ $0) and the
|
||||
* @match_spill slot fell to the 16B default. SILENT cs≠ww miscompile
|
||||
* (cstage N_MATCH reads the checker-stamped s->type for every
|
||||
* scrutinee shape, cmd/w6c/cgen.c:7510); regex fold-2a's
|
||||
* `match (re.insts[i])` is the consumer that surfaced it. The fix
|
||||
* resolves the element type from the stamped .type_ (the #45/#67
|
||||
* stamped-carrier pattern), plus the load-half twin: cgindex's
|
||||
* generic-fallback tagged-element load was the only arm missing the
|
||||
* `slot > 24` R8 word (cgen.c:9106-9117), so a >24B-slot element via
|
||||
* a non-ident base under-read the cursor.
|
||||
*
|
||||
* Rows pin: the discovering repro shape (field-base slice index, all
|
||||
* three variants matched both ways), the regex shape (56B-slot
|
||||
* inst-like union over r.insts[i]; payload reads stay within the 32B
|
||||
* cursor — words past R8 are #43's deferred residual), a chained-dot
|
||||
* base (o.in_.xs[i] — depth independence), and the ident
|
||||
* scrutinee / ident-base array + slice index controls (byte-id at
|
||||
* master per the #48 scoping probes; cs is untouched by the fix, so
|
||||
* the per-row cs==ww cmp pins them unchanged). A call-BASE index
|
||||
* (`mk()[0]`) is NOT covered: cgindex's element classification (esz +
|
||||
* tagged) skips N_CALL bases — pre-existing sibling, filed separately.
|
||||
* Per row: w6c vs w6c_ww byte-id (rule 10 — cstage is the
|
||||
* runtime-correct reference) + runtime via both drivers.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want;
|
||||
};
|
||||
|
||||
#define TU_TYPES \
|
||||
"type va = void;\n" \
|
||||
"type vb = bool;\n" \
|
||||
"type vc = size;\n" \
|
||||
"type tu = (va | vb | vc);\n"
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* The discovering repro: slice field of a struct, indexed.
|
||||
* Pre-#48 wwstage dispatched the vb element to the FIRST arm
|
||||
* regardless of pattern (tag compared against 0) — exit 1. The
|
||||
* third match pins arm-order independence (variant 1 matched
|
||||
* from a non-first arm). */
|
||||
{ "field_slice_idx",
|
||||
TU_TYPES
|
||||
"type holder = struct { xs: []tu, n: size };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let sl: []tu = [(true: vb), ((7: size): vc)];\n"
|
||||
" let h: holder;\n"
|
||||
" h.xs = sl;\n"
|
||||
" h.n = 2;\n"
|
||||
" match (h.xs[0]) {\n"
|
||||
" case let b: vb => { if (!(b: bool)) { return 2; }; };\n"
|
||||
" case => return 1;\n"
|
||||
" };\n"
|
||||
" match (h.xs[1]) {\n"
|
||||
" case let s: vc => { if ((s: size) != 7) { return 4; }; };\n"
|
||||
" case => return 3;\n"
|
||||
" };\n"
|
||||
" match (h.xs[0]) {\n"
|
||||
" case va => return 5;\n"
|
||||
" case let b: vb => { if (!(b: bool)) { return 6; }; };\n"
|
||||
" case vc => return 7;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* The regex fold-2a shape: 56B-slot inst-like union (8B tag +
|
||||
* 48B widest payload) matched over a struct-field slice index.
|
||||
* The big variant drives slot/spill sizing past 24B (the R8
|
||||
* word both in cgindex's fallback load and cgmatch's spill);
|
||||
* matched payloads read word 1 only — within the 32B cursor. */
|
||||
{ "regex_inst_56b",
|
||||
"type big = struct { a: i64, b: i64, c: i64, d: i64, e: i64, f: i64 };\n"
|
||||
"type ilit = struct { r: i64 };\n"
|
||||
"type imatch = void;\n"
|
||||
"type inst = (big | ilit | imatch);\n"
|
||||
"type re = struct { insts: []inst, n: i64 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let sl: []inst;\n"
|
||||
" let l: ilit = ilit { r = 65 };\n"
|
||||
" let v: inst = l;\n"
|
||||
" append(sl, v);\n"
|
||||
" let m: inst = void: imatch;\n"
|
||||
" append(sl, m);\n"
|
||||
" let r: re;\n"
|
||||
" r.insts = sl;\n"
|
||||
" r.n = 2;\n"
|
||||
" match (r.insts[0]) {\n"
|
||||
" case let x: ilit => { if (x.r != 65) { return 2; }; };\n"
|
||||
" case => return 1;\n"
|
||||
" };\n"
|
||||
" match (r.insts[1]) {\n"
|
||||
" case imatch => { };\n"
|
||||
" case => return 3;\n"
|
||||
" };\n"
|
||||
" match (r.insts[0]) {\n"
|
||||
" case big => return 4;\n"
|
||||
" case let x: ilit => { if (x.r != 65) { return 5; }; };\n"
|
||||
" case imatch => return 6;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Dot-DEPTH: the index base is a CHAINED dot (o.in_.xs[i] =
|
||||
* N_INDEX over N_DOT over N_DOT) — pins that the stamped-carrier
|
||||
* resolve is depth-independent (any non-ident base shape, not
|
||||
* just one dot level). */
|
||||
{ "dot_depth_idx",
|
||||
TU_TYPES
|
||||
"type inner = struct { xs: []tu, k: size };\n"
|
||||
"type outer = struct { in_: inner, n: size };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let sl: []tu = [(true: vb), ((9: size): vc)];\n"
|
||||
" let o: outer;\n"
|
||||
" o.in_.xs = sl;\n"
|
||||
" o.in_.k = 1;\n"
|
||||
" o.n = 2;\n"
|
||||
" match (o.in_.xs[0]) {\n"
|
||||
" case let b: vb => { if (!(b: bool)) { return 2; }; };\n"
|
||||
" case => return 1;\n"
|
||||
" };\n"
|
||||
" match (o.in_.xs[1]) {\n"
|
||||
" case va => return 3;\n"
|
||||
" case let s: vc => { if ((s: size) != 9) { return 4; }; };\n"
|
||||
" case vb => return 5;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* Controls: ident scrutinee + ident-base array and slice index
|
||||
* — the pre-existing matchscrutt/cgmatch paths the fix must not
|
||||
* disturb (asm byte-id at master per the scoping probes). */
|
||||
{ "ident_and_identbase_controls",
|
||||
TU_TYPES
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: tu = (true: vb);\n"
|
||||
" match (v) {\n"
|
||||
" case let b: vb => { if (!(b: bool)) { return 2; }; };\n"
|
||||
" case => return 1;\n"
|
||||
" };\n"
|
||||
" let arr: [2]tu = [(true: vb), ((7: size): vc)];\n"
|
||||
" match (arr[1]) {\n"
|
||||
" case let s: vc => { if ((s: size) != 7) { return 4; }; };\n"
|
||||
" case => return 3;\n"
|
||||
" };\n"
|
||||
" let sl: []tu = [(true: vb), ((7: size): vc)];\n"
|
||||
" match (sl[0]) {\n"
|
||||
" case let b: vb => { if (!(b: bool)) { return 6; }; };\n"
|
||||
" case => return 5;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
};
|
||||
|
||||
static const char *g_bin;
|
||||
|
||||
static int
|
||||
compile_s(const char *tool, const char *src, const char *outpath)
|
||||
{
|
||||
char cmd[1024];
|
||||
snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2>&1",
|
||||
g_bin, tool, src, outpath);
|
||||
return runwait(cmd);
|
||||
}
|
||||
|
||||
static int
|
||||
file_eq(const char *a, const char *b)
|
||||
{
|
||||
char cmd[1024];
|
||||
snprintf(cmd, sizeof cmd, "cmp -s %s %s", a, b);
|
||||
return runwait(cmd) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const char *tmpdir, const char *src,
|
||||
const char *label)
|
||||
{
|
||||
char outbin[256], cmd[1024];
|
||||
snprintf(outbin, sizeof outbin, "%s/%s_bin", tmpdir, driver);
|
||||
snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1",
|
||||
g_bin, driver, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
label, driver);
|
||||
return -1;
|
||||
}
|
||||
return runwait(outbin);
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
static char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
g_bin = bin;
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
const struct row *r = &rows[i];
|
||||
char tmpdir[128], src[160], cs_s[160], ww_s[160], rmcmd[192];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/matchnidx_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/matchnidx_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(cs_s, sizeof cs_s, "%s/matchnidx_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ww_s, sizeof ww_s, "%s/matchnidx_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return 1; }
|
||||
fputs("package main;\n\n", f);
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
total++;
|
||||
int cs_rc = compile_s("w6c", src, cs_s);
|
||||
int ww_rc = compile_s("w6c_ww", src, ww_s);
|
||||
if (cs_rc != 0 || ww_rc != 0) {
|
||||
fprintf(stderr, "FAIL row[%s]: compile rc cs=%d "
|
||||
"ww=%d\n", r->label, cs_rc, ww_rc);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
if (!file_eq(cs_s, ww_s)) {
|
||||
fprintf(stderr, "FAIL row[%s]: cs != ww .s\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
int got_cs = run_driver("ww", tmpdir, src, r->label);
|
||||
if (got_cs != r->want) {
|
||||
fprintf(stderr, "FAIL row[%s] cstage: want %d "
|
||||
"got %d\n", r->label, r->want, got_cs);
|
||||
fail++;
|
||||
}
|
||||
char wwdrv[600];
|
||||
snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin);
|
||||
if (access(wwdrv, X_OK) == 0) {
|
||||
int got_ww = run_driver("ww_ww", tmpdir, src,
|
||||
r->label);
|
||||
if (got_ww != r->want) {
|
||||
fprintf(stderr, "FAIL row[%s] wwstage: "
|
||||
"want %d got %d\n",
|
||||
r->label, r->want, got_ww);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
if (fail) {
|
||||
fprintf(stderr, "match_nonident_idx_run: %d/%d rows failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("match_nonident_idx_run: %d rows ok\n", total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,198 +0,0 @@
|
||||
/*
|
||||
* 931_variant_typekey_run — Class B semantic sentinel for #66
|
||||
* (Phase-N step 3, the user-ruled type-keying flip).
|
||||
*
|
||||
* Locks in the behavioral capability the corpus does NOT exercise:
|
||||
* distinguishing two co-variants that share a SHAPE (and, pre-flip,
|
||||
* a name key) by NOMINAL TYPE identity. cstage's cg_variant_match
|
||||
* (cmd/w6c/cgen.c:451) is what keeps `(str | linerr)` separable even
|
||||
* though `type linerr = !str` unwraps to str; #66 brings wwstage's
|
||||
* flatvariantidx to the same typeeq discipline (per-decl TY_NAMED ptr).
|
||||
*
|
||||
* Discriminating shape: a tagged union with the alias variant FIRST,
|
||||
* widened from a value with no surface type name (a call return).
|
||||
* Pre-#66 wwstage routed the value→tag direction through rhstargetname
|
||||
* (surface name) + a str/scalar shape fallback that picked the FIRST
|
||||
* shape-matching variant — i.e. the leading alias — so a plain `str`
|
||||
* from a call was tagged as `linerr` (idx 0) instead of `str` (idx 1).
|
||||
* A subsequent match then fired the wrong arm. cstage always type-keyed,
|
||||
* so pre-#66 this was a cstage-vs-wwstage divergence; each row exits 10
|
||||
* on pre-#66 wwstage and 0 on cstage / post-#66 wwstage. The 990-997
|
||||
* byte-id gates do not cover this because the corpus has no
|
||||
* same-shape co-variant whose name key and type key disagree.
|
||||
*
|
||||
* Pure runtime test: build through both drivers (cstage `ww`, wwstage
|
||||
* `ww_ww`) and assert each arm fires for its matching input.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src; /* single-module program — written as p.ww */
|
||||
int want;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* str axis. `(linerr | str)` with linerr = !str, alias first.
|
||||
* classify(0) widens a str returned by a call (no surface name)
|
||||
* — pre-#66 the shape fallback tags it as the leading str-shape
|
||||
* variant linerr (idx 0) and the match returns 1 not 2.
|
||||
* classify(1) widens a linerr (round-trip soundness). */
|
||||
{ "linerr_str_alias_first_callret",
|
||||
"package main;\n"
|
||||
"type linerr = !str;\n"
|
||||
"fn getstr() str = { return \"hi\"; };\n"
|
||||
"fn produce(k: i32) (linerr | str) = {\n"
|
||||
" if (k == 0) { return getstr(); };\n"
|
||||
" let e: linerr = \"boom\";\n"
|
||||
" return e;\n"
|
||||
"};\n"
|
||||
"export fn classify(k: i32) i32 = {\n"
|
||||
" match (produce(k)) {\n"
|
||||
" case let er: linerr => return 1;\n"
|
||||
" case let v: str => return 2;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (classify(0) != 2) { return 10; };\n"
|
||||
" if (classify(1) != 1) { return 11; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* scalar axis. `(ec | i32)` with ec = !i32, alias first.
|
||||
* classify(0) widens an i32 returned by a call — pre-#66 the
|
||||
* scalar shape fallback tags it as the leading scalar-shape
|
||||
* variant ec (idx 0). Confirms the flip isn't str-specific. */
|
||||
{ "errint_int_alias_first_callret",
|
||||
"package main;\n"
|
||||
"type ec = !i32;\n"
|
||||
"fn getint() i32 = { return 7; };\n"
|
||||
"fn produce(k: i32) (ec | i32) = {\n"
|
||||
" if (k == 0) { return getint(); };\n"
|
||||
" let e: ec = 9;\n"
|
||||
" return e;\n"
|
||||
"};\n"
|
||||
"export fn classify(k: i32) i32 = {\n"
|
||||
" match (produce(k)) {\n"
|
||||
" case let er: ec => return 1;\n"
|
||||
" case let v: i32 => return 2;\n"
|
||||
" };\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" if (classify(0) != 2) { return 10; };\n"
|
||||
" if (classify(1) != 1) { return 11; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
};
|
||||
|
||||
static int
|
||||
write_file(const char *dir, const char *name, const char *body)
|
||||
{
|
||||
char path[512];
|
||||
snprintf(path, sizeof path, "%s/%s", dir, name);
|
||||
FILE *f = fopen(path, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(body, f);
|
||||
fclose(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[96], cmd[2048];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/vtk_%d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
|
||||
if (write_file(tmpdir, "p.ww", r->src) != 0) return -1;
|
||||
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"cd %s && %s build p.ww 2>/dev/null", tmpdir, driver);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
|
||||
runwait(cmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char outbin[160];
|
||||
snprintf(outbin, sizeof outbin, "%s/p", tmpdir);
|
||||
int got = runwait(outbin);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "rm -rf %s", tmpdir);
|
||||
runwait(cmd);
|
||||
return got;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[640];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
char wdrv[640];
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *path; int gated_on_existence; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated_on_existence
|
||||
&& access(drivers[d].path, X_OK) != 0) {
|
||||
fprintf(stderr,
|
||||
"variant_typekey_run: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].path);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
int got = run_driver(drivers[d].path, &rows[i], i);
|
||||
total++;
|
||||
if (got != rows[i].want) {
|
||||
fprintf(stderr,
|
||||
"variant_typekey_run[%s][%s]: exit=%d want=%d\n",
|
||||
drivers[d].name, rows[i].label,
|
||||
got, rows[i].want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr,
|
||||
"variant_typekey_run: %d/%d fixtures failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("variant_typekey_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,679 +0,0 @@
|
||||
/*
|
||||
* 938_tagged_structlit_payload_run — #23 (fold-5b prereq): tagged-union
|
||||
* CONSTRUCTION from an INLINE struct-literal payload whose fields are
|
||||
* themselves tagged ((void|size) — the regex inst_repeat shape,
|
||||
* ref/hare/regex/regex.ha:55-60). Pre-#23 the widen choke-point's
|
||||
* struct-payload arm (cg_widen_tagged_store / cgwidentaggedstorebp)
|
||||
* carried its OWN inline N_STRUCTLIT field loop — a parallel fill that
|
||||
* drifted from cg_structlit_fill / cgstructlitfill: it had float/str/
|
||||
* slice/scalar arms but NO tagged-field widen arm, so a (void|T)-typed
|
||||
* field's raw scalar landed in the field's TAG word (silent truncation
|
||||
* past the first tagged field; both stages, byte-id — gate-blind;
|
||||
* prober-9 PG5 evidence, re-confirmed at 6699158). The fix deletes the
|
||||
* parallel loop and delegates to the canonical fill, which also
|
||||
* inherits the nested-struct field arm.
|
||||
*
|
||||
* row | shape | want
|
||||
* ---------------------+----------------------------------------+-----
|
||||
* inst_repeat_shape | rep{id,origin,min=void,max=5:size} — |
|
||||
* | the PG5b repro; 48B payload | 0
|
||||
* rep3_allsize | {id,min,max} all set via `: size` |
|
||||
* | casts — PG5g; tag+payload word checks | 0
|
||||
* tagged_only16 | single (void|size) field — 16B payload | 0
|
||||
* prebound_tagged_field| min = mn (pre-bound (void|size) LOCAL |
|
||||
* | — tagged→tagged subset inside the fill)| 0
|
||||
* str_and_tagged | {s:str, m:(void|size)} — 3-word str |
|
||||
* | arm + tagged arm in one literal | 0
|
||||
* variant_pos0 | union (rep|lit) — struct variant tag 0 | 0
|
||||
* slice_elem_cursorfit | 24B payload through append + indexed |
|
||||
* | match (union 32B = exactly the 4-word |
|
||||
* | tagged cursor; >32B indexed reads are |
|
||||
* | the open over-cap cursor residual, |
|
||||
* | task #37 — NOT pinned here) | 0
|
||||
* nested_struct_field | nested struct-typed field literal in a |
|
||||
* | union payload — delegation-inherited |
|
||||
* | recursion arm (pre-#23: AX-only store) | 0
|
||||
* ellipsis_autofill | `...` autofill + tagged field — the |
|
||||
* | fill's TK_ELLIPSIS zero-fill runs at |
|
||||
* | the widened slot's payload base | 0
|
||||
* two_tagged_offset0 | tagged field at struct OFFSET 0, a 2nd |
|
||||
* | tagged field, then a trailing scalar | 0
|
||||
* nested_and_tagged | nested struct field AND tagged field |
|
||||
* | in ONE literal — fill→widen→fill, both |
|
||||
* | recursion directions in one shape | 0
|
||||
* voidstr_field | (void|str) tagged field — 3-word str |
|
||||
* | payload through the fill's widen arm; |
|
||||
* | pre-#23 the garbage tag matched NO |
|
||||
* | variant (fallthrough-proof readback) | 0
|
||||
* recursion_torture | 3-level tagged(struct{tagged(struct{ |
|
||||
* | tagged(leaf-lit)})}) — the widen↔fill |
|
||||
* | mutual recursion terminates + correct | 0
|
||||
* payload8_boundary | struct payload exactly 8B (single size |
|
||||
* | field) — smallest-payload guard | 0
|
||||
* scalar_neighbor | scalar+f64+str payload, NO tagged |
|
||||
* | field — emission no-regress guard | 0
|
||||
*
|
||||
* Field readback goes through MATCH BINDINGS only: direct field reads
|
||||
* on a struct-w/-tagged-field LOCAL are the open #24 (wwstage
|
||||
* field(SB) fallback). Tagged-field inits use explicit `: size`
|
||||
* casts in the #23 rows; the BARE untyped-int form (the old #33
|
||||
* wwstage mis-tag) is pinned by its own row (untyped_int_bare_widen)
|
||||
* since the cgvariantmatch untyped arm landed. The chained-dot
|
||||
* tagged-leaf read/assign family (#38a) is pinned by the
|
||||
* chained_* rows below since the dot-spine tagged arms landed.
|
||||
*
|
||||
* Every K_RUN row also asserts cstage/wwstage asm byte-id. NNN<950,
|
||||
* self-contained (/tmp, no imports) — rule-14's selfhost-sibling race
|
||||
* does not apply (936/941 precedent).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa), cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* noasm: skip the cs/ww byte-id check for rows whose READBACK needs an
|
||||
* inner `match` on a tagged FIELD scrutinee — wwstage routes that load
|
||||
* through a 4-word cursor scratch where cstage copies field words
|
||||
* directly (PRE-EXISTING divergence, confirmed at master 6699158 on
|
||||
* the same sources; construction asm is byte-id up to the readback —
|
||||
* the regex-arc match-on-tagged-struct-field family, needs a filed
|
||||
* task). The skip is printed loudly so it can't rot silently. */
|
||||
struct row { const char *label; const char *src; int want; int noasm; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* the PG5b repro verbatim: only id/origin survived pre-#23
|
||||
* (min's tag word read 0 = void by accident, max's read 5). */
|
||||
{ "inst_repeat_shape",
|
||||
"package main;\n"
|
||||
"type inst_lit = rune;\n"
|
||||
"type rep = struct {\n"
|
||||
" id: size,\n"
|
||||
" origin: size,\n"
|
||||
" min: (void | size),\n"
|
||||
" max: (void | size),\n"
|
||||
"};\n"
|
||||
"type inst = (inst_lit | rep);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: inst = rep { id = 1: size, origin = 2: size, min = void, max = 5: size };\n"
|
||||
" match (v) {\n"
|
||||
" case let ir: rep => {\n"
|
||||
" if (ir.id != 1) { return 1; };\n"
|
||||
" if (ir.origin != 2) { return 2; };\n"
|
||||
" if (!(ir.min is void)) { return 3; };\n"
|
||||
" if (!(ir.max is size)) { return 4; };\n"
|
||||
" if (ir.max as size != 5) { return 5; };\n"
|
||||
" };\n"
|
||||
" case let l: inst_lit => { return 6; };\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* PG5g: every tagged field set — pre-#23 the raw 2/5 landed in
|
||||
* the tag words and both is-tests failed. */
|
||||
{ "rep3_allsize",
|
||||
"package main;\n"
|
||||
"type inst_lit = rune;\n"
|
||||
"type rep3 = struct { id: size, min: (void | size), max: (void | size) };\n"
|
||||
"type inst = (inst_lit | rep3);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: inst = rep3 { id = 1: size, min = 2: size, max = 5: size };\n"
|
||||
" match (v) {\n"
|
||||
" case let ir: rep3 => {\n"
|
||||
" if (ir.id != 1) { return 1; };\n"
|
||||
" if (!(ir.min is size)) { return 2; };\n"
|
||||
" if (ir.min as size != 2) { return 3; };\n"
|
||||
" if (!(ir.max is size)) { return 4; };\n"
|
||||
" if (ir.max as size != 5) { return 5; };\n"
|
||||
" };\n"
|
||||
" case let l: inst_lit => { return 6; };\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* minimal payload: the tagged field IS the whole struct. */
|
||||
{ "tagged_only16",
|
||||
"package main;\n"
|
||||
"type only = struct { m: (void | size) };\n"
|
||||
"type u = (rune | only);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: u = only { m = 7: size };\n"
|
||||
" match (v) {\n"
|
||||
" case let o: only => {\n"
|
||||
" if (!(o.m is size)) { return 1; };\n"
|
||||
" if (o.m as size != 7) { return 2; };\n"
|
||||
" };\n"
|
||||
" case => return 3;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* field value is a pre-bound tagged LOCAL — the fill's widen arm
|
||||
* takes the tagged→tagged identity-copy route. */
|
||||
{ "prebound_tagged_field",
|
||||
"package main;\n"
|
||||
"type inst_lit = rune;\n"
|
||||
"type rep3 = struct { id: size, min: (void | size), max: (void | size) };\n"
|
||||
"type inst = (inst_lit | rep3);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let mn: (void | size) = 2: size;\n"
|
||||
" let v: inst = rep3 { id = 3: size, min = mn, max = void };\n"
|
||||
" match (v) {\n"
|
||||
" case let ir: rep3 => {\n"
|
||||
" if (ir.id != 3) { return 1; };\n"
|
||||
" if (!(ir.min is size)) { return 2; };\n"
|
||||
" if (ir.min as size != 2) { return 3; };\n"
|
||||
" if (!(ir.max is void)) { return 4; };\n"
|
||||
" };\n"
|
||||
" case let l: inst_lit => { return 5; };\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* str field (3-word arm) and tagged field in the same literal —
|
||||
* the arms must not disturb each other's offsets. */
|
||||
{ "str_and_tagged",
|
||||
"package main;\n"
|
||||
"type inst_lit = rune;\n"
|
||||
"type named = struct { s: str, m: (void | size) };\n"
|
||||
"type inst = (inst_lit | named);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: inst = named { s = \"hi\", m = 9: size };\n"
|
||||
" match (v) {\n"
|
||||
" case let nm: named => {\n"
|
||||
" if (len(nm.s) != 2) { return 1; };\n"
|
||||
" if (!(nm.m is size)) { return 2; };\n"
|
||||
" if (nm.m as size != 9) { return 3; };\n"
|
||||
" };\n"
|
||||
" case let l: inst_lit => { return 4; };\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* struct variant FIRST in the union — tag 0; pins that the fix
|
||||
* is position-independent. */
|
||||
{ "variant_pos0",
|
||||
"package main;\n"
|
||||
"type inst_lit = rune;\n"
|
||||
"type rep3 = struct { id: size, min: (void | size), max: (void | size) };\n"
|
||||
"type inst = (rep3 | inst_lit);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: inst = rep3 { id = 1: size, min = void, max = 8: size };\n"
|
||||
" match (v) {\n"
|
||||
" case let ir: rep3 => {\n"
|
||||
" if (ir.id != 1) { return 1; };\n"
|
||||
" if (!(ir.min is void)) { return 2; };\n"
|
||||
" if (!(ir.max is size)) { return 3; };\n"
|
||||
" if (ir.max as size != 8) { return 4; };\n"
|
||||
" };\n"
|
||||
" case let l: inst_lit => { return 5; };\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* construction → append → indexed match, end-to-end. Payload 24B
|
||||
* keeps the union at 32B — exactly the 4-word tagged cursor, so
|
||||
* the indexed read is in-cap. The >32B indexed read truncates at
|
||||
* the cursor (open over-cap residual, #22b/#36 family) — the
|
||||
* 48B-payload rows above therefore match the LOCAL only. */
|
||||
{ "slice_elem_cursorfit",
|
||||
"package main;\n"
|
||||
"type inst_lit = rune;\n"
|
||||
"type rep1 = struct { id: size, m: (void | size) };\n"
|
||||
"type inst = (inst_lit | rep1);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let insts: []inst = [];\n"
|
||||
" append(insts, ('a': inst_lit));\n"
|
||||
" let v: inst = rep1 { id = 4: size, m = 6: size };\n"
|
||||
" append(insts, v);\n"
|
||||
" if (len(insts) != 2) { return 1; };\n"
|
||||
" match (insts[1]) {\n"
|
||||
" case let r: rep1 => {\n"
|
||||
" if (r.id != 4) { return 2; };\n"
|
||||
" if (!(r.m is size)) { return 3; };\n"
|
||||
" if (r.m as size != 6) { return 4; };\n"
|
||||
" };\n"
|
||||
" case let l: inst_lit => { return 5; };\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* nested struct-typed field literal inside the union payload —
|
||||
* rides the canonical fill's recursion arm the delegation
|
||||
* inherits (pre-#23 the parallel loop stored AX only). */
|
||||
{ "nested_struct_field",
|
||||
"package main;\n"
|
||||
"type inner = struct { a: size, b: size };\n"
|
||||
"type outer = struct { id: size, in_: inner };\n"
|
||||
"type u = (rune | outer);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: u = outer { id = 1: size, in_ = inner { a = 2: size, b = 3: size } };\n"
|
||||
" match (v) {\n"
|
||||
" case let o: outer => {\n"
|
||||
" if (o.id != 1) { return 1; };\n"
|
||||
" if (o.in_.a != 2) { return 2; };\n"
|
||||
" if (o.in_.b != 3) { return 3; };\n"
|
||||
" };\n"
|
||||
" case => return 4;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* `...` autofill + tagged field (ken n7): the fill's TK_ELLIPSIS
|
||||
* zero-fill loop must run at the widened slot's payload base, not
|
||||
* the slot base — pre-#23 exit 3 (raw 5 in m's tag word). */
|
||||
{ "ellipsis_autofill",
|
||||
"package main;\n"
|
||||
"type st = struct { a: u64, m: (void | size) };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e: (void | st) = st { m = 5: size, ... };\n"
|
||||
" match (e) {\n"
|
||||
" case let s: st => {\n"
|
||||
" if (s.a != 0u64) { return 2; };\n"
|
||||
" if (!(s.m is size)) { return 3; };\n"
|
||||
" if (s.m as size != 5) { return 4; };\n"
|
||||
" };\n"
|
||||
" case void => { return 9; };\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* tagged field at struct OFFSET 0 plus a second tagged field and
|
||||
* a TRAILING scalar (ken n2b) — pins offset-0 tag placement and
|
||||
* the scalar offset computed past two 16B tagged fields. */
|
||||
{ "two_tagged_offset0",
|
||||
"package main;\n"
|
||||
"type d2 = struct { m: (void | size), n: (void | i64), z: u64 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e: (void | d2) = d2 { m = 4: size, n = (-9): i64, z = 77u64 };\n"
|
||||
" match (e) {\n"
|
||||
" case let s: d2 => {\n"
|
||||
" if (!(s.m is size)) { return 2; };\n"
|
||||
" if (s.m as size != 4) { return 3; };\n"
|
||||
" if (!(s.n is i64)) { return 4; };\n"
|
||||
" if (s.n as i64 != -9) { return 5; };\n"
|
||||
" if (s.z != 77u64) { return 6; };\n"
|
||||
" };\n"
|
||||
" case void => { return 9; };\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* nested struct field AND tagged field in the SAME literal (ken
|
||||
* n5b) — exercises fill→fill (nested) and fill→widen (tagged)
|
||||
* from one delegated call. */
|
||||
{ "nested_and_tagged",
|
||||
"package main;\n"
|
||||
"type inn = struct { v: u64 };\n"
|
||||
"type outt = struct { i: inn, m: (void | size) };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e: (void | outt) = outt { i = inn { v = 11u64 }, m = 8: size };\n"
|
||||
" match (e) {\n"
|
||||
" case let s: outt => {\n"
|
||||
" if (s.i.v != 11u64) { return 2; };\n"
|
||||
" if (!(s.m is size)) { return 3; };\n"
|
||||
" if (s.m as size != 8) { return 4; };\n"
|
||||
" };\n"
|
||||
" case void => { return 9; };\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* (void|str) tagged field — 3-word str payload through the fill's
|
||||
* widen arm. Readback initialises `got = 9` BEFORE the inner
|
||||
* match so a garbage tag matching NO variant can't fall through
|
||||
* to a fake 0 (pre-#23 the raw str ptr landed in the tag word
|
||||
* and exactly that fallthrough faked a pass). */
|
||||
{ "voidstr_field",
|
||||
"package main;\n"
|
||||
"type st = struct { id: size, s: (void | str) };\n"
|
||||
"type u = (rune | st);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: u = st { id = 3: size, s = \"hello\" };\n"
|
||||
" match (v) {\n"
|
||||
" case let o: st => {\n"
|
||||
" if (o.id != 3) { return 1; };\n"
|
||||
" let got: i32 = 9;\n"
|
||||
" match (o.s) {\n"
|
||||
" case let sv: str => {\n"
|
||||
" if (len(sv) == 5) { got = 0; } else { got = 2; };\n"
|
||||
" };\n"
|
||||
" case void => { got = 3; };\n"
|
||||
" };\n"
|
||||
" return got;\n"
|
||||
" };\n"
|
||||
" case => return 4;\n"
|
||||
" };\n"
|
||||
" return 5;\n"
|
||||
"};\n", 0, 1 },
|
||||
/* 3-level widen↔fill mutual recursion: tagged(struct{tagged(
|
||||
* struct{tagged(struct-lit)})}) — termination AND correctness of
|
||||
* the #23 delegation cycle (pre-#23 exit 4: mid's tag dropped). */
|
||||
{ "recursion_torture",
|
||||
"package main;\n"
|
||||
"type leaf = struct { p: size };\n"
|
||||
"type t1 = (void | leaf);\n"
|
||||
"type mid = struct { t: t1 };\n"
|
||||
"type t2 = (void | mid);\n"
|
||||
"type outr = struct { id: size, t: t2 };\n"
|
||||
"type u = (rune | outr);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: u = outr { id = 1: size, t = mid { t = leaf { p = 9: size } } };\n"
|
||||
" match (v) {\n"
|
||||
" case let o: outr => {\n"
|
||||
" if (o.id != 1) { return 1; };\n"
|
||||
" match (o.t) {\n"
|
||||
" case let m: mid => {\n"
|
||||
" match (m.t) {\n"
|
||||
" case let l: leaf => {\n"
|
||||
" if (l.p != 9) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
" };\n"
|
||||
" case void => { return 3; };\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
" case void => { return 4; };\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
" case => return 5;\n"
|
||||
" };\n"
|
||||
" return 6;\n"
|
||||
"};\n", 0, 1 },
|
||||
/* struct payload exactly 8B — the smallest payload through the
|
||||
* widen→fill delegation (boundary guard; worked pre-#23 too). */
|
||||
{ "payload8_boundary",
|
||||
"package main;\n"
|
||||
"type one = struct { a: size };\n"
|
||||
"type u = (rune | one);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: u = one { a = 42: size };\n"
|
||||
" match (v) {\n"
|
||||
" case let o: one => { if (o.a != 42) { return 1; }; };\n"
|
||||
" case => return 2;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* no tagged field at all — the previously-working scalar/float/
|
||||
* str arms must emit unchanged through the delegation. */
|
||||
{ "scalar_neighbor",
|
||||
"package main;\n"
|
||||
"type plain = struct { a: size, f: f64, s: str };\n"
|
||||
"type u = (rune | plain);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: u = plain { a = 11: size, f = 2.5, s = \"xyz\" };\n"
|
||||
" match (v) {\n"
|
||||
" case let p: plain => {\n"
|
||||
" if (p.a != 11) { return 1; };\n"
|
||||
" if (p.f != 2.5) { return 2; };\n"
|
||||
" if (len(p.s) != 3) { return 3; };\n"
|
||||
" };\n"
|
||||
" case => return 4;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
/* #33: a BARE untyped-int init into (void|T) — pre-fix wwstage's
|
||||
* variant scan fell to the str/slice shape fallback whose first
|
||||
* non-str/slice variant is `void` (tag 0 stored, is-test expects
|
||||
* 1; cstage resolved via type_assignable). Pins let-init, assign
|
||||
* after void, arg widen, the bool-leading skip, and the signed
|
||||
* (void|i64) variant (ken hB3); the cast form rides alongside as
|
||||
* the no-drift control (it was always right). */
|
||||
/* #38a: chained-dot TAGGED leaf — the dot-spine walkers (value
|
||||
* chain + ptr chain, both stages) had no TY_TAGGED leaf arm, so
|
||||
* READS pulled one word (tag; as/match consumers read stale DX
|
||||
* payload, ken x5c want-28-got-20) and ASSIGNS stored the rhs
|
||||
* over the TAG slot (ken x5d). Six rows: literal-init read,
|
||||
* assign-then-read, depth-3 chain read-before-write (ken hB1),
|
||||
* (void|str) 32B box incl. void-over-str write (ken hB2),
|
||||
* *outer root, mid-chain *rep. The INDEX-spine sibling
|
||||
* (xs[i].min) is filed separately (task #58) — NOT a row here. */
|
||||
{ "chained_tagged_literal_read",
|
||||
"package main;\n"
|
||||
"type rep = struct { id: size, min: (void | size), name: (void | str) };\n"
|
||||
"type outer = struct { tag: size, r: rep };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let o = outer { tag = 4: size, r = rep { id = 6: size, min = 8: size, name = void } };\n"
|
||||
" if (o.tag != 4) { return 1; };\n"
|
||||
" if (o.r.id != 6) { return 2; };\n"
|
||||
" if (!(o.r.min is size)) { return 3; };\n"
|
||||
" if (o.r.min as size != 8) { return 4; };\n"
|
||||
" if (!(o.r.name is void)) { return 5; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
{ "chained_tagged_assign",
|
||||
"package main;\n"
|
||||
"type rep = struct { id: size, min: (void | size) };\n"
|
||||
"type outer = struct { tag: size, r: rep };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let o: outer = outer { tag = 4: size, r = rep { id = 6: size, min = void } };\n"
|
||||
" o.r.min = 8: size;\n"
|
||||
" if (!(o.r.min is size)) { return 1; };\n"
|
||||
" if (o.r.min as size != 8) { return 2; };\n"
|
||||
" o.r.min = 9;\n"
|
||||
" if (!(o.r.min is size)) { return 3; };\n"
|
||||
" if (o.r.min as size != 9) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
{ "chained_tagged_depth2",
|
||||
"package main;\n"
|
||||
"type rep = struct { id: size, min: (void | size) };\n"
|
||||
"type mid = struct { m: size, r: rep };\n"
|
||||
"type outer = struct { tag: size, w: mid };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let o = outer { tag = 4: size, w = mid { m = 2: size, r = rep { id = 6: size, min = 5: size } } };\n"
|
||||
" if (!(o.w.r.min is size)) { return 5; };\n"
|
||||
" if (o.w.r.min as size != 5) { return 6; };\n"
|
||||
" o.w.r.min = 8: size;\n"
|
||||
" if (o.w.m != 2) { return 1; };\n"
|
||||
" if (o.w.r.id != 6) { return 2; };\n"
|
||||
" if (!(o.w.r.min is size)) { return 3; };\n"
|
||||
" if (o.w.r.min as size != 8) { return 4; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
{ "chained_tagged_strvariant",
|
||||
"package main;\n"
|
||||
"type rep = struct { id: size, nm: (void | str) };\n"
|
||||
"type outer = struct { tag: size, r: rep };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let o = outer { tag = 4: size, r = rep { id = 6: size, nm = void } };\n"
|
||||
" o.r.nm = \"hello\";\n"
|
||||
" if (!(o.r.nm is str)) { return 1; };\n"
|
||||
" if ((o.r.nm as str).len != 5) { return 2; };\n"
|
||||
" match (o.r.nm) {\n"
|
||||
" case let s: str => { if (s.len != 5) { return 3; }; };\n"
|
||||
" case void => { return 4; };\n"
|
||||
" };\n"
|
||||
" o.r.nm = void;\n"
|
||||
" if (!(o.r.nm is void)) { return 5; };\n"
|
||||
" if (o.r.id != 6) { return 6; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
{ "chained_tagged_ptrroot",
|
||||
"package main;\n"
|
||||
"type rep = struct { id: size, min: (void | size) };\n"
|
||||
"type outer = struct { tag: size, r: rep };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let o = outer { tag = 4: size, r = rep { id = 6: size, min = void } };\n"
|
||||
" let p: *outer = &o;\n"
|
||||
" p.r.min = 8: size;\n"
|
||||
" if (p.r.id != 6) { return 1; };\n"
|
||||
" if (!(p.r.min is size)) { return 2; };\n"
|
||||
" if (p.r.min as size != 8) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
{ "chained_tagged_ptrmid",
|
||||
"package main;\n"
|
||||
"type rep = struct { id: size, min: (void | size) };\n"
|
||||
"type outer = struct { tag: size, p: *rep };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let r = rep { id = 6: size, min = void };\n"
|
||||
" let o = outer { tag = 4: size, p = &r };\n"
|
||||
" o.p.min = 8: size;\n"
|
||||
" if (o.p.id != 6) { return 1; };\n"
|
||||
" if (!(o.p.min is size)) { return 2; };\n"
|
||||
" if (o.p.min as size != 8) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
{ "untyped_int_bare_widen",
|
||||
"package main;\n"
|
||||
"fn take(v: (void | size)) i32 = {\n"
|
||||
" if (v is size) { return (v as size): i32; };\n"
|
||||
" return -1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e: (void | size) = 5;\n"
|
||||
" if (!(e is size)) { return 1; };\n"
|
||||
" if (e as size != 5) { return 2; };\n"
|
||||
" let mn: (void | size) = void;\n"
|
||||
" mn = 7;\n"
|
||||
" if (!(mn is size)) { return 3; };\n"
|
||||
" if (mn as size != 7) { return 4; };\n"
|
||||
" let b: (bool | u64) = 9;\n"
|
||||
" if (!(b is u64)) { return 5; };\n"
|
||||
" if (take(9) != 9) { return 6; };\n"
|
||||
" let cf: (void | size) = 5: size;\n"
|
||||
" if (!(cf is size)) { return 7; };\n"
|
||||
" if (cf as size != 5) { return 8; };\n"
|
||||
" let f: (void | i64) = 9;\n"
|
||||
" if (!(f is i64)) { return 9; };\n"
|
||||
" if (f as i64 != 9) { return 10; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0 },
|
||||
};
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[96], src[128], outbin[128], errf[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/tsp_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/tsp_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/tsp_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(errf, sizeof errf, "%s/err", tmpdir);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s >/dev/null 2>%s",
|
||||
driver, outbin, src, errf);
|
||||
int brc = runwait(cmd);
|
||||
if (brc != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
if (got != r->want) {
|
||||
fprintf(stderr, "row[%s]: %s exit %d, want %d\n",
|
||||
r->label, driver, got, r->want);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* cs==ww .s byte-id (rule 10). */
|
||||
static int
|
||||
asm_byte_identical(const char *bin, const struct row *r, int i)
|
||||
{
|
||||
char src[96], cs[96], ws[96], cmd[1024];
|
||||
snprintf(src, sizeof src, "/tmp/tsp_asm_%d_%d.ww", getpid(), i);
|
||||
snprintf(cs, sizeof cs, "/tmp/tsp_asm_%d_%d_c.s", getpid(), i);
|
||||
snprintf(ws, sizeof ws, "/tmp/tsp_asm_%d_%d_w.s", getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
|
||||
unlink(src);
|
||||
return -1;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
|
||||
bin, ws, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
|
||||
unlink(src); unlink(cs);
|
||||
return -1;
|
||||
}
|
||||
int rc = slurp_eq(cs, ws);
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
|
||||
r->label);
|
||||
unlink(src); unlink(cs); unlink(ws);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[2080];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[2120], wdrv[2120];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (run_driver(cdrv, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
if (access(wdrv, X_OK) == 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (run_driver(wdrv, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (rows[i].noasm) {
|
||||
printf("row[%s]: asm byte-id SKIPPED "
|
||||
"(pre-existing match-on-tagged-field "
|
||||
"cs/ww readback divergence, "
|
||||
"master-confirmed)\n", rows[i].label);
|
||||
continue;
|
||||
}
|
||||
total++;
|
||||
if (asm_byte_identical(bin, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "tagged_structlit_payload: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("tagged_structlit_payload: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
/*
|
||||
* 944_idx_tagged_field_run — #58 INDEXED-element TAGGED-field read+assign.
|
||||
*
|
||||
* `xs[i].f` where f is a TAGGED union field of a struct array element was
|
||||
* broken on BOTH stages, byte-IDENTICAL and SILENT (#263 gate-blind; the
|
||||
* byte-id gate cannot see it, runtime is the only net). The `arr[i].field`
|
||||
* spine had arms for array / str/slice / float but NO TY_TAGGED arm, so a
|
||||
* tagged field fell to the single-word scalar fallback:
|
||||
* READ: loaded only the tag word; the payload cursor (DX/CX/R8) was
|
||||
* never loaded -> `xs[i].f as T` read stale DX (#38a residual).
|
||||
* ASSIGN: DOUBLY broken -- stored the raw unboxed scalar into the TAG
|
||||
* slot, corrupting the box (never boxed, never wrote payload).
|
||||
* The fix inserts a TY_TAGGED arm before each scalar fallback, both
|
||||
* stages (align-BOTH, one commit). READ: AX=&xs[i] -> cursor (AX=tag,
|
||||
* DX/CX/R8=payload), tag LAST. ASSIGN: box the concrete scalar variant
|
||||
* (tag-index + payload), store over the indexed dest spine.
|
||||
*
|
||||
* row | shape | want
|
||||
* -------------+------------------------------------------------+-----
|
||||
* const_read | xs[0].m as size ((void|size), tag1) | 0
|
||||
* var_read | xs[i].m as size variable index | 0
|
||||
* tag2_read | xs[k].m as size ((void|bool|size), TAG2) | 0
|
||||
* assign_back | xs[0].m = 8:size; readback via *row ptr | 0
|
||||
*
|
||||
* Pre-fix (base cc896bd): const_read/var_read/tag2_read read stale DX
|
||||
* (payload dropped); assign_back corrupts the box (scalar-into-tag) so
|
||||
* the ptr-readback control sees the un-overwritten constructed payload
|
||||
* (99, not 8) -- all four FAIL both stages, cs==ww byte-id (the teeth).
|
||||
* MULTIPLE tag positions (tag1-of-2 + tag2-of-3) are mandatory: an
|
||||
* aligned-tag-only pin false-greens the assign scalar-into-tag-slot
|
||||
* corruption. Post-fix: all four 0/0, cs/ww still byte-identical.
|
||||
*
|
||||
* The >32B / multi-word / float-payload arms LOUD-STOP (their real emission
|
||||
* is not yet wired — #114). These shapes are NOT unconstructible: a wide-box
|
||||
* union is built via a NARROW variant, so each tripwire is REACHABLE and is
|
||||
* pinned expect-loud by the cfrows[] rows below. #114 wires the real emission
|
||||
* and graduates those rows to runtime-value pins.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa), cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want; };
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "const_read",
|
||||
"package main;\n"
|
||||
"type opt = (void | size);\n"
|
||||
"type row = struct { id: int, m: opt };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: [2]row = [\n"
|
||||
" row { id = 1, m = 7: size },\n"
|
||||
" row { id = 2, m = 8: size },\n"
|
||||
" ];\n"
|
||||
" let v: size = xs[0].m as size;\n"
|
||||
" if (v != 7) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "var_read",
|
||||
"package main;\n"
|
||||
"type opt = (void | size);\n"
|
||||
"type row = struct { id: int, m: opt };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: [2]row = [\n"
|
||||
" row { id = 1, m = 7: size },\n"
|
||||
" row { id = 2, m = 8: size },\n"
|
||||
" ];\n"
|
||||
" let i: size = 1;\n"
|
||||
" let v: size = xs[i].m as size;\n"
|
||||
" if (v != 8) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "tag2_read",
|
||||
"package main;\n"
|
||||
"type opt3 = (void | bool | size);\n"
|
||||
"type row = struct { id: int, m: opt3 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: [2]row = [\n"
|
||||
" row { id = 1, m = 5: size },\n"
|
||||
" row { id = 2, m = 9: size },\n"
|
||||
" ];\n"
|
||||
" let i: size = 1;\n"
|
||||
" let v: size = xs[i].m as size;\n"
|
||||
" if (v != 9) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "assign_back",
|
||||
"package main;\n"
|
||||
"type opt = (void | size);\n"
|
||||
"type row = struct { id: int, m: opt };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: [2]row = [\n"
|
||||
" row { id = 1, m = 99: size },\n"
|
||||
" row { id = 2, m = 2: size },\n"
|
||||
" ];\n"
|
||||
" xs[0].m = 8: size;\n"
|
||||
" let p: *row = &xs[0];\n"
|
||||
" let v: size = p.m as size;\n"
|
||||
" if (v != 8) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
};
|
||||
|
||||
/*
|
||||
* #58 LOUD-STOP TRIPWIRES — expect-compile-fail rows.
|
||||
*
|
||||
* The >32B / multi-word / float-payload arms of the indexed tagged-field
|
||||
* read+store are NOT silently miscompiled: they LOUD-STOP (rule 7). Earlier
|
||||
* triage believed these shapes were "unconstructible at HEAD" — they are NOT.
|
||||
* A wide-box union (largest variant >32B) is constructible via a NARROW/scalar
|
||||
* variant: `(void | size | [5]size)` initialised `m = 7: size` builds and runs
|
||||
* fine; only reading/assigning that field via the indexed spine reaches the
|
||||
* tripwire. So each tripwire is REACHABLE and must be pinned as expect-loud:
|
||||
* a build that SUCCEEDS here would mean the loud-stop regressed into a silent
|
||||
* miscompile. Each row must (a) fail the build and (b) emit a "#58" tripwire on
|
||||
* stderr, attributing the CFAIL to this fold (not the #54/#23 construction
|
||||
* hole, which only fires on STRUCT-LITERAL payloads). When #114 wires real
|
||||
* >32B box+memcpy emission, it replaces the tripwires AND graduates these rows
|
||||
* to runtime-value pins.
|
||||
*/
|
||||
struct cfrow { const char *label; const char *src; };
|
||||
|
||||
static const struct cfrow cfrows[] = {
|
||||
{ "big_read", /* >32B box read tripwire */
|
||||
"package main;\n"
|
||||
"type big = (void | size | [5]size);\n"
|
||||
"type row = struct { id: int, m: big };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: [2]row = [\n"
|
||||
" row { id = 1, m = 7: size },\n"
|
||||
" row { id = 2, m = 8: size },\n"
|
||||
" ];\n"
|
||||
" let v: size = xs[0].m as size;\n"
|
||||
" if (v != 7) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n" },
|
||||
{ "big_assign", /* >32B box store tripwire */
|
||||
"package main;\n"
|
||||
"type big = (void | size | [5]size);\n"
|
||||
"type row = struct { id: int, m: big };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: [2]row = [\n"
|
||||
" row { id = 1, m = 7: size },\n"
|
||||
" row { id = 2, m = 8: size },\n"
|
||||
" ];\n"
|
||||
" xs[0].m = 9: size;\n"
|
||||
" return 0;\n"
|
||||
"};\n" },
|
||||
{ "midword_assign", /* multi-word (>16B, <=32B box) store tripwire */
|
||||
"package main;\n"
|
||||
"type mid = (void | size | [3]size);\n"
|
||||
"type row = struct { id: int, m: mid };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: [2]row = [\n"
|
||||
" row { id = 1, m = 7: size },\n"
|
||||
" row { id = 2, m = 8: size },\n"
|
||||
" ];\n"
|
||||
" xs[0].m = 9: size;\n"
|
||||
" return 0;\n"
|
||||
"};\n" },
|
||||
{ "float_assign", /* float-payload store tripwire */
|
||||
"package main;\n"
|
||||
"type fo = (void | f64);\n"
|
||||
"type row = struct { id: int, m: fo };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: [2]row = [\n"
|
||||
" row { id = 1, m = 1.0: f64 },\n"
|
||||
" row { id = 2, m = 2.0: f64 },\n"
|
||||
" ];\n"
|
||||
" xs[0].m = 3.0: f64;\n"
|
||||
" return 0;\n"
|
||||
"};\n" },
|
||||
};
|
||||
|
||||
static int
|
||||
file_has(const char *path, const char *needle)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return 0;
|
||||
char buf[8192];
|
||||
size_t n = fread(buf, 1, sizeof buf - 1, f);
|
||||
fclose(f);
|
||||
buf[n] = '\0';
|
||||
return strstr(buf, needle) != NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
run_cfail(const char *driver, const struct cfrow *r, int i)
|
||||
{
|
||||
char tmpdir[96], src[160], outbin[160], errf[160], rmcmd[176], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/itf_cf_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/itf_cf_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/itf_cf_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(errf, sizeof errf, "%s/err", tmpdir);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 20 %s build -o %s %s >/dev/null 2>%s",
|
||||
driver, outbin, src, errf);
|
||||
int brc = runwait(cmd);
|
||||
int ok = (brc != 0) && file_has(errf, "#58");
|
||||
if (!ok)
|
||||
fprintf(stderr,
|
||||
"row[%s]: %s build rc=%d, want CFAIL with \"#58\" tripwire\n",
|
||||
r->label, driver, brc);
|
||||
runwait(rmcmd);
|
||||
return ok ? 0 : 1;
|
||||
}
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[96], src[160], outbin[160], errf[160], rmcmd[176], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/itf_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/itf_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/itf_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(errf, sizeof errf, "%s/err", tmpdir);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 20 %s build -o %s %s >/dev/null 2>%s",
|
||||
driver, outbin, src, errf);
|
||||
int brc = runwait(cmd);
|
||||
if (brc != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
if (got != r->want) {
|
||||
fprintf(stderr, "row[%s]: %s exit %d, want %d\n",
|
||||
r->label, driver, got, r->want);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
asm_byte_identical(const char *bin, const struct row *r, int i)
|
||||
{
|
||||
char src[96], cs[96], ws[96], cmd[1024];
|
||||
snprintf(src, sizeof src, "/tmp/itf_asm_%d_%d.ww", getpid(), i);
|
||||
snprintf(cs, sizeof cs, "/tmp/itf_asm_%d_%d_c.s", getpid(), i);
|
||||
snprintf(ws, sizeof ws, "/tmp/itf_asm_%d_%d_w.s", getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
|
||||
unlink(src);
|
||||
return -1;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
|
||||
bin, ws, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
|
||||
unlink(src); unlink(cs);
|
||||
return -1;
|
||||
}
|
||||
int rc = slurp_eq(cs, ws);
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
|
||||
r->label);
|
||||
unlink(src); unlink(cs); unlink(ws);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[2080];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[2120], wdrv[2120];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int cn = (int)(sizeof cfrows / sizeof cfrows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (run_driver(cdrv, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
/* cstage loud-stop tripwires (cgen.c) — expect CFAIL with "#58". */
|
||||
for (int i = 0; i < cn; i++) {
|
||||
total++;
|
||||
if (run_cfail(cdrv, &cfrows[i], i) != 0) fail++;
|
||||
}
|
||||
if (access(wdrv, X_OK) == 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (run_driver(wdrv, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (asm_byte_identical(bin, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
/* wwstage loud-stop tripwires (cgenexpr.ww). */
|
||||
for (int i = 0; i < cn; i++) {
|
||||
total++;
|
||||
if (run_cfail(wdrv, &cfrows[i], cn + i) != 0) fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "idx_tagged_field: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("idx_tagged_field: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,282 +0,0 @@
|
||||
/*
|
||||
* 944_tagged_widen_arg_run — #55 tagged-SOURCE arg widened into a wider
|
||||
* tagged-union PARAM slot (slot-DIFFER).
|
||||
*
|
||||
* A call arg whose own type is a NARROWER tagged union (un16=16B) passed
|
||||
* where the param is a WIDER tagged union (un3=24B). wwstage pushargsrev
|
||||
* mis-pushed every leg but the lucky aligned-ident: the slot-DIFFER arg
|
||||
* fell past the slot-gated aistagged arms to the concrete-variant scalar
|
||||
* boxing (taggedvariantindex<0 → tag clamped to 0 → word0 boxed, real tag
|
||||
* + high words dropped, NO variant remap). cstage routes EVERY tagged
|
||||
* source through cg_widen_tagged_store (cgen.c:2982 src_is_tagged) →
|
||||
* scratch + tag-remap, correct on all 8.
|
||||
*
|
||||
* The fix (ww align-UP, 3 coordinated arms in cgenutil.ww):
|
||||
* 1. the IDENT aistagged gate is now slot-gated (was ungated TRUE) so a
|
||||
* slot-DIFFER ident falls to the widen scratch instead of the no-
|
||||
* remap natural push (the prefix-luck);
|
||||
* 2. pushargsrev's widensz>0 arm routes a tagged source (argistagged)
|
||||
* through @tagscr + cgwidentaggedstore + push high→low (the
|
||||
* pname/argistuple twin);
|
||||
* 3. cgwidentaggedstore's cursor arm (INDEX/DOT ≤32B) spills by the
|
||||
* SOURCE width, zero-pads, and tag-REMAPS (mirrors cstage's
|
||||
* cg_widen_tagged_store cursor + shared pad/remap tail, cgen.c:2695-
|
||||
* 2714) — pre-#55 it spilled by the dst slot (stale high regs) and
|
||||
* skipped the remap.
|
||||
*
|
||||
* row | leg | tag | want
|
||||
* ----------------+-------+-----------+-----
|
||||
* ident_aligned | e | i64=tag0 | 42
|
||||
* deref_aligned | *p | i64=tag0 | 42
|
||||
* index_aligned | xs[0] | i64=tag0 | 42
|
||||
* dot_aligned | w.f | i64=tag0 | 42
|
||||
* ident_remap | e | i64=tag1 | 42 (un16b reorders i64→tag1)
|
||||
* deref_remap | *p | i64=tag1 | 42
|
||||
* index_remap | xs[0] | i64=tag1 | 42
|
||||
* dot_remap | w.f | i64=tag1 | 42
|
||||
*
|
||||
* The remap rows carry the MISALIGNED tag: un16b=(bool|i64) puts i64 at
|
||||
* tag1, widened into un3b=(i64|bool|s16) where i64 is tag0 — so the source
|
||||
* tag MUST be rewritten or the callee reads the wrong arm. An aligned-only
|
||||
* suite false-greens a remap-less fix (the exact trap here, ken #55).
|
||||
*
|
||||
* Pre-fix: deref/index/dot legs cs42/ww!=42 (silent wrong); aligned-ident
|
||||
* ww42 by LUCK but asm cs!=ww. Post-fix: all 8 cs42/ww42, all 8 byte-id.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa), cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct row { const char *label; const char *src; int want; };
|
||||
|
||||
#define TAKE_ALIGNED \
|
||||
"type s16 = struct { a: i64, b: i64 };\n" \
|
||||
"type un16 = (i64 | bool);\n" \
|
||||
"type un3 = (i64 | bool | s16);\n" \
|
||||
"fn take(e: un3) i64 = {\n" \
|
||||
" match (e) {\n" \
|
||||
" case let v: i64 => { return v; };\n" \
|
||||
" case let b: bool => { return -1; };\n" \
|
||||
" case let s: s16 => { return s.a + s.b; };\n" \
|
||||
" };\n" \
|
||||
"};\n"
|
||||
|
||||
#define TAKE_REMAP \
|
||||
"type s16 = struct { a: i64, b: i64 };\n" \
|
||||
"type un16b = (bool | i64);\n" \
|
||||
"type un3b = (i64 | bool | s16);\n" \
|
||||
"fn take(e: un3b) i64 = {\n" \
|
||||
" match (e) {\n" \
|
||||
" case let v: i64 => { return v; };\n" \
|
||||
" case let b: bool => { return -1; };\n" \
|
||||
" case let s: s16 => { return s.a + s.b; };\n" \
|
||||
" };\n" \
|
||||
"};\n"
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "ident_aligned",
|
||||
"package main;\n" TAKE_ALIGNED
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: un16 = (42: i64);\n"
|
||||
" if (take(v) != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "deref_aligned",
|
||||
"package main;\n" TAKE_ALIGNED
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: un16 = (42: i64);\n"
|
||||
" let p: *un16 = &v;\n"
|
||||
" if (take(*p) != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "index_aligned",
|
||||
"package main;\n" TAKE_ALIGNED
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: [2]un16 = [(42: i64), (7: i64)];\n"
|
||||
" if (take(xs[0]) != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "dot_aligned",
|
||||
"package main;\n" TAKE_ALIGNED
|
||||
"type wrap = struct { f: un16 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let w: wrap = wrap { f = (42: i64) };\n"
|
||||
" if (take(w.f) != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
/* misaligned-tag rows — i64 is tag1 in un16b, tag0 in un3b: the
|
||||
* remap MUST fire or the callee reads the wrong match arm. */
|
||||
{ "ident_remap",
|
||||
"package main;\n" TAKE_REMAP
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: un16b = (42: i64);\n"
|
||||
" if (take(v) != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "deref_remap",
|
||||
"package main;\n" TAKE_REMAP
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: un16b = (42: i64);\n"
|
||||
" let p: *un16b = &v;\n"
|
||||
" if (take(*p) != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "index_remap",
|
||||
"package main;\n" TAKE_REMAP
|
||||
"export fn main() i32 = {\n"
|
||||
" let xs: [2]un16b = [(42: i64), (7: i64)];\n"
|
||||
" if (take(xs[0]) != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
{ "dot_remap",
|
||||
"package main;\n" TAKE_REMAP
|
||||
"type wrap = struct { f: un16b };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let w: wrap = wrap { f = (42: i64) };\n"
|
||||
" if (take(w.f) != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0 },
|
||||
};
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char src[128], tmpdir[96], errf[128], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/twa_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/twa_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/twa_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(errf, sizeof errf, "%s/err", tmpdir);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 20 %s build -o %s %s >/dev/null 2>%s",
|
||||
driver, outbin, src, errf);
|
||||
int brc = runwait(cmd);
|
||||
if (brc != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
if (got != r->want) {
|
||||
fprintf(stderr, "row[%s]: %s exit %d, want %d\n",
|
||||
r->label, driver, got, r->want);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
asm_byte_identical(const char *bin, const struct row *r, int i)
|
||||
{
|
||||
char src[96], cs[96], ws[96], cmd[1024];
|
||||
snprintf(src, sizeof src, "/tmp/twa_asm_%d_%d.ww", getpid(), i);
|
||||
snprintf(cs, sizeof cs, "/tmp/twa_asm_%d_%d_c.s", getpid(), i);
|
||||
snprintf(ws, sizeof ws, "/tmp/twa_asm_%d_%d_w.s", getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
|
||||
unlink(src);
|
||||
return -1;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
|
||||
bin, ws, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
|
||||
unlink(src); unlink(cs);
|
||||
return -1;
|
||||
}
|
||||
int rc = slurp_eq(cs, ws);
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
|
||||
r->label);
|
||||
unlink(src); unlink(cs); unlink(ws);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[2080];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[2120], wdrv[2120];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (run_driver(cdrv, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
if (access(wdrv, X_OK) == 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (run_driver(wdrv, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
if (asm_byte_identical(bin, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "tagged_widen_arg: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("tagged_widen_arg: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,74 +1,23 @@
|
||||
/*
|
||||
* 944_variant_chain_b95_run — #95: variant-match chain-membership arm
|
||||
* (pass 1b) + structural fallback (c2), BOTH stages fused (cstage
|
||||
* cg_tag_for_variant + wwstage flatvariantidxt are the same route).
|
||||
* Pre-#95 a NAMED struct source that was not pointer-identical to a
|
||||
* NAMED variant fell through every pass and the widen stored tag 0 —
|
||||
* both stages, byte-identical, gate-blind (the worst class on the
|
||||
* board: silent wrong tag on VALID code).
|
||||
* 944_variant_chain_b95_run -- SLIMMED to the one irreducible asymmetric row.
|
||||
*
|
||||
* Row sources are ken's #95 first-position oracle
|
||||
* (.ai/ken-95-oracle.md, probes /tmp/b95/src + kb4/kb5) and rob's
|
||||
* fold spec (.ai/rob-95-spec.md). Reference: harec
|
||||
* tagged_select_subtype P1/P2/P3 (ref/harec/src/types.c:702-739,
|
||||
* :988-1140).
|
||||
* The #95 variant-match chain-membership corpus (22 rows) migrated to fold-2
|
||||
* homes (#5-C5): the K_RUN value rows -> test/lang/variant_chain_b95_test.ww
|
||||
* (@test + T2 byte-id); the two #81-byte-id-waived CALL-source rows ->
|
||||
* test/lang/variant_chain_b95_runonly_test.ww (value-only); the K_BUILDERR
|
||||
* reject rows -> test/wcc/data/vchain_<name>/case.ww runww //ww:error carriers.
|
||||
*
|
||||
* row | shape (oracle row) | cs/ww
|
||||
* --------------------+-------------------------------------+------
|
||||
* chain_1lvl_i | kb5_v2s1i HEADLINE: depth-1 ident |
|
||||
* | src base → (void|al0) — was both- |
|
||||
* | wrong-IDENTICAL byte-id tag 0 | 0/0
|
||||
* chain_2lvl_i | kb95_2lvl_i: ident src depth-2 — |
|
||||
* | the class extends to any depth | 0/0
|
||||
* chain_deep_src | kb95_deep_src: src ali2 → (void| |
|
||||
* | ali) — chain-direction DOWN | 0/0
|
||||
* chain_deep_var | kb95_deep_var: src base → (void| |
|
||||
* | ali2) — chain-direction UP | 0/0
|
||||
* chain_call_bound81 | kb5_v2s1: CALL src 1-lvl — runs |
|
||||
* | 0/0; byte-id waived: pre-existing |
|
||||
* | #81 uninit-local zero-fill asm |
|
||||
* | noise (NO at the #95 base too) | 0/0
|
||||
* chain_call2_bound81 | kb4_v2_struct2 (#95's original): |
|
||||
* | CALL src 2-lvl — same #81 waiver | 0/0
|
||||
* chain_amb_loud | kb95_amb: (ali|ali2) over one base, |
|
||||
* | src base — TODAY silently tagged |
|
||||
* | member 0; ≥2 chain hits cannot be |
|
||||
* | disambiguated in the nominal-lossy |
|
||||
* | model (harec nassign≥2 → NULL) — |
|
||||
* | hard-error, twin texts share the |
|
||||
* | experr tail | err/err
|
||||
* chain_amb_srcA/B | src ali / src ali2 into (ali|ali2): |
|
||||
* | pass-1 EXACT fires first (harec P1 |
|
||||
* | short-circuit) — precedence pins, |
|
||||
* | MUST NOT MOVE | 0/0
|
||||
* nom_str / nom_err | (str|linerr) #218 nominal pins: str |
|
||||
* | src tags str, linerr src tags |
|
||||
* | linerr — MUST NOT MOVE through the |
|
||||
* | new arm | 0/0
|
||||
* exact_ctl | kb5_v2sE2: exact-match ident |
|
||||
* | control — MUST NOT MOVE | 0/0
|
||||
* bare_ctl/bare_2lvl | kb4_v2_ctrl / kb4_v2_alias2: pass-2 |
|
||||
* | bare-source controls — MUST NOT |
|
||||
* | MOVE | 0/0
|
||||
* bare_ambig/2 | kb4_v2_ambig(2): pass-2 ambiguity |
|
||||
* | guards hold their loud texts | err/err
|
||||
* callret_bound277 | kb5_v2sE: CALL src typed-as-alias — |
|
||||
* | ww #277 aggregate-return capability |
|
||||
* | leg, OUT of #95; cells pinned as |
|
||||
* | observed (cs runs, ww loud) | 0/err
|
||||
* unrel_struct | kb95_unrel (c2): ta/tb nominally- |
|
||||
* | unrelated same-layout structs, src |
|
||||
* | ta → (void|tb) — was both-wrong- |
|
||||
* | identical byte-id; harec interning |
|
||||
* | makes dealias(ta)==dealias(tb) → |
|
||||
* | accept, tag tb (types.c:72-81 + |
|
||||
* | :1000-1002) | 0/0
|
||||
* callret_bound277 (kb5_v2sE) CANNOT move: it is cstage-runs / wwstage-rejects
|
||||
* -- cstage builds+runs it (exit 0) while wwstage LOUD-STOPS with "deferred
|
||||
* #277" (the ww aggregate-return capability gap, OUT of #95). That asymmetry is
|
||||
* inexpressible as a single-compile @test (which builds one way) or a runww
|
||||
* //ww:error carrier (which demands BOTH stages reject). It stays a slim C pin.
|
||||
*
|
||||
* K_RUN rows build+run BOTH drivers (exit==want) and assert cstage/
|
||||
* wwstage asm byte-id. K_RUN_NOID waives byte-id — cite the blocking
|
||||
* task at the row. K_RUN_CS_WWERR: cstage build+run exit==cswant AND
|
||||
* wwstage build FAILS with experr (944_alias_accept_run kind). NNN<950,
|
||||
* self-contained /tmp sources, no imports (944 precedent).
|
||||
* Non-vacuity (the pin CAN go RED): if cstage stops running it (exit != 0) or
|
||||
* wwstage starts accepting it (#277 wired), the row fails; the wwstage leg also
|
||||
* requires the EXACT "deferred #277" diagnostic so an unrelated wwstage error
|
||||
* cannot masquerade as coverage. Mutation-checked: dropping the cast-to-alias
|
||||
* (`mk()` typed plainly) flips cstage and was observed RED.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -86,36 +35,6 @@ runwait(const char *cmd)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int
|
||||
slurp_eq(const char *a, const char *b)
|
||||
{
|
||||
FILE *fa = fopen(a, "rb");
|
||||
FILE *fb = fopen(b, "rb");
|
||||
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
|
||||
int rc = 0;
|
||||
for (;;) {
|
||||
int ca = fgetc(fa), cb = fgetc(fb);
|
||||
if (ca != cb) { rc = -1; break; }
|
||||
if (ca == EOF) break;
|
||||
}
|
||||
fclose(fa); fclose(fb);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#define K_RUN 0 /* build+run BOTH, exits==wants, + byte-id */
|
||||
#define K_BUILDERR 1 /* build FAILS with experr on BOTH drivers */
|
||||
#define K_RUN_NOID 2 /* build+run BOTH, exits==wants, byte-id
|
||||
* SKIPPED — cite the blocking task */
|
||||
#define K_RUN_CS_WWERR 3 /* bound row: cstage build+run exit==cswant;
|
||||
* wwstage build FAILS with experr — cite
|
||||
* the blocking task */
|
||||
|
||||
struct row { const char *label; const char *src;
|
||||
int cswant; int wwwant; int kind; const char *experr; };
|
||||
|
||||
/* errlog_has — a builderr cell must fail WITH its diagnostic; any other
|
||||
* failure (parse error, crash, hang-kill) is a vacuous reject (940
|
||||
* precedent). */
|
||||
static int
|
||||
errlog_has(const char *path, const char *needle)
|
||||
{
|
||||
@@ -128,390 +47,68 @@ errlog_has(const char *path, const char *needle)
|
||||
return strstr(buf, needle) != NULL;
|
||||
}
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* ---- c1: chain-membership graduations (were tag-0 silent) */
|
||||
{ "chain_1lvl_i",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type al0 = base;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: base;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" let v: (void | al0) = s;\n"
|
||||
" if (!(v is al0)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
{ "chain_2lvl_i",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type al0 = base;\n"
|
||||
"type ali = al0;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: base;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" let v: (void | ali) = s;\n"
|
||||
" if (!(v is ali)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
{ "chain_deep_src",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type ali = base;\n"
|
||||
"type ali2 = ali;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: ali2;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" let v: (void | ali) = s;\n"
|
||||
" if (!(v is ali)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
{ "chain_deep_var",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type ali = base;\n"
|
||||
"type ali2 = ali;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: base;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" let v: (void | ali2) = s;\n"
|
||||
" if (!(v is ali2)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
/* CALL-src legs run 0/0; byte-id waived: the mk() frame carries
|
||||
* the pre-existing #81 uninit-local zero-fill asm divergence
|
||||
* (cells were byteid=NO at the #95 base too). */
|
||||
{ "chain_call_bound81",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type al0 = base;\n"
|
||||
"fn mk() base = {\n"
|
||||
" let s: base;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" return s;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: (void | al0) = mk();\n"
|
||||
" if (!(v is al0)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN_NOID, NULL }, /* byte-id: task #81 */
|
||||
{ "chain_call2_bound81",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type al0 = base;\n"
|
||||
"type ali = al0;\n"
|
||||
"fn mk() base = {\n"
|
||||
" let s: base;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" return s;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: (void | ali) = mk();\n"
|
||||
" if (!(v is ali)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN_NOID, NULL }, /* byte-id: task #81 */
|
||||
/* ≥2 chain hits — was a SILENT member-0 tag; twin texts share
|
||||
* the experr tail (prefix convention). */
|
||||
{ "chain_amb_loud",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type ali = base;\n"
|
||||
"type ali2 = ali;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: base;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" let v: (ali | ali2) = s;\n"
|
||||
" if (v is ali) { return 1; };\n"
|
||||
" if (v is ali2) { return 2; };\n"
|
||||
" return 3;\n"
|
||||
"};\n", 0, 0, K_BUILDERR,
|
||||
"source alias chain reaches >=2 variants" },
|
||||
/* pass-1 EXACT precedence pins: an exact source distinguishes
|
||||
* (ali|ali2) — the chain arm must stay BEHIND pass 1. */
|
||||
{ "chain_amb_srcA",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type ali = base;\n"
|
||||
"type ali2 = ali;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: ali;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" let v: (ali | ali2) = s;\n"
|
||||
" if (!(v is ali)) { return 1; };\n"
|
||||
" if (v is ali2) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
{ "chain_amb_srcB",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type ali = base;\n"
|
||||
"type ali2 = ali;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: ali2;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" let v: (ali | ali2) = s;\n"
|
||||
" if (!(v is ali2)) { return 1; };\n"
|
||||
" if (v is ali) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
/* #218 nominal regression pins (rob §3e): (str | linerr) stays
|
||||
* distinguishable through the new arm. */
|
||||
{ "nom_str",
|
||||
"package main;\n"
|
||||
"type linerr = str;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: str = \"ok\";\n"
|
||||
" let v: (str | linerr) = s;\n"
|
||||
" if (!(v is str)) { return 1; };\n"
|
||||
" if (v is linerr) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
{ "nom_err",
|
||||
"package main;\n"
|
||||
"type linerr = str;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e: linerr = \"bad\";\n"
|
||||
" let v: (str | linerr) = e;\n"
|
||||
" if (!(v is linerr)) { return 1; };\n"
|
||||
" if (v is str) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
{ "exact_ctl",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type al0 = base;\n"
|
||||
"type ali = al0;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: ali;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" let v: (void | ali) = s;\n"
|
||||
" if (!(v is ali)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
/* ---- pass-2 bare-source controls (the #15 leg stays put) */
|
||||
{ "bare_ctl",
|
||||
"package main;\n"
|
||||
"type vt = struct { a: int };\n"
|
||||
"type stream = *vt;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: vt;\n"
|
||||
" x.a = 7;\n"
|
||||
" let v: (void | stream) = &x;\n"
|
||||
" if (!(v is stream)) { return 1; };\n"
|
||||
" match (v) {\n"
|
||||
" case let s: stream => { if (s.a != 7) { return 2; }; };\n"
|
||||
" case void => { return 3; };\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
{ "bare_2lvl",
|
||||
"package main;\n"
|
||||
"type vt = struct { a: int };\n"
|
||||
"type st0 = *vt;\n"
|
||||
"type stream = st0;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: vt;\n"
|
||||
" x.a = 7;\n"
|
||||
" let v: (void | stream) = &x;\n"
|
||||
" if (!(v is stream)) { return 1; };\n"
|
||||
" match (v) {\n"
|
||||
" case let s: stream => { if (s.a != 7) { return 2; }; };\n"
|
||||
" case void => { return 3; };\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
{ "bare_ambig",
|
||||
"package main;\n"
|
||||
"type p1 = *i32;\n"
|
||||
"type p2 = *i32;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: i32 = 7;\n"
|
||||
" let v: (p1 | p2) = &x;\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_BUILDERR,
|
||||
"bare source structurally matches >=2 NAMED variants" },
|
||||
{ "bare_ambig2",
|
||||
"package main;\n"
|
||||
"type b1 = *i32;\n"
|
||||
"type b2 = *i32;\n"
|
||||
"type q1 = b1;\n"
|
||||
"type q2 = b2;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let x: i32 = 7;\n"
|
||||
" let v: (q1 | q2) = &x;\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_BUILDERR,
|
||||
"bare source structurally matches >=2 NAMED variants" },
|
||||
/* ww #277 aggregate-return capability leg — OUT of #95; dual
|
||||
* cells pinned as observed so neither side drifts silently. */
|
||||
{ "callret_bound277",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type al0 = base;\n"
|
||||
"type ali = al0;\n"
|
||||
"fn mk() ali = {\n"
|
||||
" let s: ali;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" return s;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: (void | ali) = mk();\n"
|
||||
" if (!(v is ali)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN_CS_WWERR,
|
||||
"deferred #277" }, /* ww: task #277 */
|
||||
/* ---- c2: structural fallback — was both-wrong-identical
|
||||
* byte-id silent (live metric-1: checkers accept, cgen tagged
|
||||
* 0). harec accepts by interning (one node, tag tb). */
|
||||
{ "unrel_struct",
|
||||
"package main;\n"
|
||||
"type ta = struct { a: int, b: int };\n"
|
||||
"type tb = struct { a: int, b: int };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: ta;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" let v: (void | tb) = s;\n"
|
||||
" if (!(v is tb)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_RUN, NULL },
|
||||
/* ---- c3 (#107): is/as ACCEPTANCE must stay nominal-exact. cgen's
|
||||
* chain/structural tag-synthesis arms widen tag lookup at the WIDEN
|
||||
* site only; the wwstage is/as gate (check.ww:4677) reuses
|
||||
* flatvariantidxt and, pre-c3, the widening leaked into ACCEPTANCE
|
||||
* (cs!=ww) and surfaced the >=2 cgen fatal mid-check. c3 added an
|
||||
* exact-only mode for that caller. These pin BOTH stages REJECTING
|
||||
* (cstage's independent gate check.c:2036 already does; wwstage now
|
||||
* matches it) — NOT a crash for the ambiguity shape. Shared experr
|
||||
* substring "not a variant" (cstage "X is not a variant of (...)",
|
||||
* wwstage "is/as: not a variant of operand (X)"). */
|
||||
{ "isas_chain_reject",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type ali = base;\n"
|
||||
"fn f(v: (void | ali)) i32 = {\n"
|
||||
" if (v is base) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = { return 0; };\n",
|
||||
0, 0, K_BUILDERR, "not a variant" },
|
||||
{ "isas_unrel_reject",
|
||||
"package main;\n"
|
||||
"type ta = struct { a: int, b: int };\n"
|
||||
"type tb = struct { a: int, b: int };\n"
|
||||
"fn f(v: (void | tb)) i32 = {\n"
|
||||
" if (v is ta) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = { return 0; };\n",
|
||||
0, 0, K_BUILDERR, "not a variant" },
|
||||
{ "isas_amb_reject_notcrash",
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type ali = base;\n"
|
||||
"type ali2 = ali;\n"
|
||||
"fn f(v: (ali | ali2)) i32 = {\n"
|
||||
" if (v is base) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = { return 0; };\n",
|
||||
0, 0, K_BUILDERR, "not a variant" },
|
||||
/* ---- rob's obligated mixed prim/alias TWIN: union (int | ai)
|
||||
* ai=int, source typed aj=int — under all-variants counting both the
|
||||
* bare `int` variant and `ai` share the int bottom -> the cgen widen
|
||||
* (full mode) hard-errors (harec nassign>=2 -> NULL). Pinned LOUD
|
||||
* BOTH stages (the chain-ambiguity path; #95 deviation-2 ratified). */
|
||||
{ "twin_prim_alias_amb",
|
||||
"package main;\n"
|
||||
"type ai = int;\n"
|
||||
"type aj = int;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let s: aj = 7;\n"
|
||||
" let v: (int | ai) = s;\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, 0, K_BUILDERR,
|
||||
"source alias chain reaches >=2 variants" },
|
||||
};
|
||||
/* kb5_v2sE: a CALL whose result is typed as the alias ali, widened into
|
||||
* (void | ali). cstage's aggregate-return path runs it; wwstage #277 rejects. */
|
||||
static const char *src =
|
||||
"package main;\n"
|
||||
"type base = struct { a: int, b: int };\n"
|
||||
"type al0 = base;\n"
|
||||
"type ali = al0;\n"
|
||||
"fn mk() ali = {\n"
|
||||
" let s: ali;\n"
|
||||
" s.a = 4; s.b = 9;\n"
|
||||
" return s;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: (void | ali) = mk();\n"
|
||||
" if (!(v is ali)) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n";
|
||||
|
||||
static const char *experr = "deferred #277";
|
||||
|
||||
/* Build src with `driver`; if expect_err, the build must FAIL with experr on
|
||||
* stderr. Otherwise build must succeed, then the binary runs and its exit must
|
||||
* equal `want`. Returns 0 on the expected outcome, 1 otherwise. */
|
||||
static int
|
||||
run_driver(const char *driver, const struct row *r, int i, int expect_err,
|
||||
int want)
|
||||
check(const char *driver, int expect_err, int want)
|
||||
{
|
||||
char tmpdir[96], src[128], outbin[128], errf[128], rmcmd[160];
|
||||
char cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/vc95_%d_d_%d", getpid(), i);
|
||||
char tmpdir[96], srcp[128], outbin[128], errf[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/vc277_%d_%d", getpid(), expect_err);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/vc95_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/vc95_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(srcp, sizeof srcp, "%s/c.ww", tmpdir);
|
||||
snprintf(outbin, sizeof outbin, "%s/c", tmpdir);
|
||||
snprintf(errf, sizeof errf, "%s/err", tmpdir);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -1; }
|
||||
fputs(r->src, f);
|
||||
FILE *f = fopen(srcp, "wb");
|
||||
if (!f) { runwait(rmcmd); return 1; }
|
||||
fputs(src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd,
|
||||
"timeout 20 %s build -o %s %s >/dev/null 2>%s",
|
||||
driver, outbin, src, errf);
|
||||
snprintf(cmd, sizeof cmd, "timeout 20 %s build -o %s %s >/dev/null 2>%s",
|
||||
driver, outbin, srcp, errf);
|
||||
int brc = runwait(cmd);
|
||||
|
||||
int rc = 0;
|
||||
if (expect_err) {
|
||||
int ok = (brc != 0)
|
||||
&& (r->experr == NULL || errlog_has(errf, r->experr));
|
||||
if (!ok)
|
||||
fprintf(stderr, "row[%s]: %s expected loud builderr "
|
||||
"\"%s\" (brc=%d)\n", r->label, driver,
|
||||
r->experr ? r->experr : "", brc);
|
||||
runwait(rmcmd);
|
||||
return ok ? 0 : 1;
|
||||
if (brc == 0 || !errlog_has(errf, experr)) {
|
||||
fprintf(stderr, "callret_bound277: %s expected loud "
|
||||
"builderr \"%s\" (brc=%d)\n", driver, experr, brc);
|
||||
rc = 1;
|
||||
}
|
||||
} else if (brc != 0) {
|
||||
fprintf(stderr, "callret_bound277: build via %s failed\n", driver);
|
||||
rc = 1;
|
||||
} else {
|
||||
int got = runwait(outbin);
|
||||
if (got != want) {
|
||||
fprintf(stderr, "callret_bound277: %s exit %d, want %d\n",
|
||||
driver, got, want);
|
||||
rc = 1;
|
||||
}
|
||||
}
|
||||
if (brc != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
r->label, driver);
|
||||
runwait(rmcmd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
if (got != want) {
|
||||
fprintf(stderr, "row[%s]: %s exit %d, want %d\n",
|
||||
r->label, driver, got, want);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
asm_byte_identical(const char *bin, const struct row *r, int i)
|
||||
{
|
||||
char src[96], cs[96], ws[96], cmd[1024];
|
||||
snprintf(src, sizeof src, "/tmp/vc95_asm_%d_%d.ww", getpid(), i);
|
||||
snprintf(cs, sizeof cs, "/tmp/vc95_asm_%d_%d_c.s", getpid(), i);
|
||||
snprintf(ws, sizeof ws, "/tmp/vc95_asm_%d_%d_w.s", getpid(), i);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) return -1;
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c -o %s %s 2>/dev/null", bin, cs, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c errored\n", r->label);
|
||||
unlink(src);
|
||||
return -1;
|
||||
}
|
||||
snprintf(cmd, sizeof cmd, "%s/w6c_ww -o %s %s 2>/dev/null",
|
||||
bin, ws, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: w6c_ww errored\n", r->label);
|
||||
unlink(src); unlink(cs);
|
||||
return -1;
|
||||
}
|
||||
int rc = slurp_eq(cs, ws);
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "row[%s]: cstage vs wwstage asm differs\n",
|
||||
r->label);
|
||||
unlink(src); unlink(cs); unlink(ws);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -532,36 +129,15 @@ main(void)
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int cs_err = rows[i].kind == K_BUILDERR;
|
||||
if (run_driver(cdrv, &rows[i], i, cs_err,
|
||||
rows[i].cswant) != 0) fail++;
|
||||
}
|
||||
if (access(wdrv, X_OK) == 0) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int ww_err = rows[i].kind == K_BUILDERR
|
||||
|| rows[i].kind == K_RUN_CS_WWERR;
|
||||
if (run_driver(wdrv, &rows[i], i, ww_err,
|
||||
rows[i].wwwant) != 0) fail++;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (rows[i].kind != K_RUN)
|
||||
continue;
|
||||
total++;
|
||||
if (asm_byte_identical(bin, &rows[i], i) != 0) fail++;
|
||||
}
|
||||
}
|
||||
int fail = 0;
|
||||
fail += check(cdrv, 0, 0); /* cstage runs, exit 0 */
|
||||
if (access(wdrv, X_OK) == 0)
|
||||
fail += check(wdrv, 1, 0); /* wwstage loud-stops #277 */
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "variant_chain_b95: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
fprintf(stderr, "variant_chain_b95: %d checks failed\n", fail);
|
||||
return 1;
|
||||
}
|
||||
printf("variant_chain_b95: %d/%d ok\n", total, total);
|
||||
printf("variant_chain_b95: callret_bound277 pin ok\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,287 +0,0 @@
|
||||
/*
|
||||
* 949_void_error_singleton_run — #140: a `!void` error-singleton used
|
||||
* as a VALUE is a tag-only variant. Its void payload has size 0 and no
|
||||
* storage, so cgexpr must materialise NOTHING — only the enclosing
|
||||
* widen arm stamps the variant tag.
|
||||
*
|
||||
* Pre-fix cstage's cgexpr N_IDENT path fell through to the generic
|
||||
* global-value load and emitted `MOVQ main.<name>(SB), AX` for a
|
||||
* payload symbol that is never defined:
|
||||
* w6l: undefined reference to 'main.too_long'
|
||||
* wwstage already emitted tag-only (no symbol load) — the runtime-
|
||||
* correct reference. The fix aligns cstage UP (ken-139 oracle).
|
||||
*
|
||||
* Every singleton-value position the cgexpr-ident root feeds is
|
||||
* covered (enumerated at HEAD — all four link-failed identically):
|
||||
* return, let-init, assign, call-arg. Rows also vary the union width
|
||||
* (8B scalar payload vs a >8B str payload, which exercises the
|
||||
* tag-return CX/R8 zeroing) and check BOTH the error-arm and the
|
||||
* success-arm tags so a mis-stamped tag flips the exit code.
|
||||
*
|
||||
* Three checks per row:
|
||||
* - byte-id: w6c vs w6c_ww .s must be identical (rule 10).
|
||||
* - no-bogus-load: the cstage .s must NOT reference `<name>(SB)`
|
||||
* for the singleton type-name (the pre-fix undefined symbol).
|
||||
* - runtime: build via the ww / ww_ww drivers (the build step is
|
||||
* the link, which is the pre-fix teeth) and run; the exit code
|
||||
* pins the variant tag.
|
||||
*
|
||||
* This is half of the path-c1 pin (union with #141's def-dim file).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want; /* expected exit code */
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* return-position: the void singleton returned from an 8B-payload
|
||||
* union; match selects the singleton arm (exit 0). */
|
||||
{ "return_singleton",
|
||||
"type too_long = !void;\n"
|
||||
"fn f() (i64 | too_long) = {\n"
|
||||
" return too_long;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" match (f()) {\n"
|
||||
" case let v: i64 => return 1;\n"
|
||||
" case too_long => return 0;\n"
|
||||
" };\n"
|
||||
" return 2;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* same fn shape, success variant: pins the tag discriminates
|
||||
* (the success arm must run, returning the payload). */
|
||||
{ "return_success",
|
||||
"type too_long = !void;\n"
|
||||
"fn f() (i64 | too_long) = {\n"
|
||||
" return 42i64;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" match (f()) {\n"
|
||||
" case let v: i64 => { if (v != 42) { return 3; }; return 0; };\n"
|
||||
" case too_long => return 1;\n"
|
||||
" };\n"
|
||||
" return 2;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* let-init: `let e: (i64 | too_long) = too_long;`. */
|
||||
{ "letinit_singleton",
|
||||
"type too_long = !void;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e: (i64 | too_long) = too_long;\n"
|
||||
" match (e) {\n"
|
||||
" case let v: i64 => return 1;\n"
|
||||
" case too_long => return 0;\n"
|
||||
" };\n"
|
||||
" return 2;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* assign: reassign an existing union local to the void singleton
|
||||
* (cg_widen_tagged_store source = the singleton ident). */
|
||||
{ "assign_singleton",
|
||||
"type too_long = !void;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let e: (i64 | too_long) = 5i64;\n"
|
||||
" e = too_long;\n"
|
||||
" match (e) {\n"
|
||||
" case let v: i64 => return 1;\n"
|
||||
" case too_long => return 0;\n"
|
||||
" };\n"
|
||||
" return 2;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* call-arg: the singleton passed into a tagged parameter. */
|
||||
{ "callarg_singleton",
|
||||
"type too_long = !void;\n"
|
||||
"fn use(r: (i64 | too_long)) i32 = {\n"
|
||||
" match (r) {\n"
|
||||
" case let v: i64 => return 1;\n"
|
||||
" case too_long => return 0;\n"
|
||||
" };\n"
|
||||
" return 2;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" return use(too_long);\n"
|
||||
"};\n",
|
||||
0 },
|
||||
/* wide union: a >8B (str) success payload makes the tagged return
|
||||
* slot exceed one word, exercising the CX/R8 zeroing on the
|
||||
* tag-only singleton return. Error arm selected. */
|
||||
{ "return_singleton_wide",
|
||||
"type too_long = !void;\n"
|
||||
"fn f(ok: bool) (str | too_long) = {\n"
|
||||
" if (ok) { return \"hello\"; };\n"
|
||||
" return too_long;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" match (f(false)) {\n"
|
||||
" case let s: str => return 1;\n"
|
||||
" case too_long => { };\n"
|
||||
" };\n"
|
||||
" match (f(true)) {\n"
|
||||
" case let s: str => { if (s.len != 5) { return 2; }; };\n"
|
||||
" case too_long => return 3;\n"
|
||||
" };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
};
|
||||
|
||||
static const char *g_bin;
|
||||
|
||||
static int
|
||||
compile_s(const char *tool, const char *src, const char *outpath,
|
||||
const char *errpath)
|
||||
{
|
||||
char cmd[1024];
|
||||
snprintf(cmd, sizeof cmd, "%s/%s %s > %s 2> %s",
|
||||
g_bin, tool, src, outpath, errpath);
|
||||
return runwait(cmd);
|
||||
}
|
||||
|
||||
static int
|
||||
file_eq(const char *a, const char *b)
|
||||
{
|
||||
char cmd[1024];
|
||||
snprintf(cmd, sizeof cmd, "cmp -s %s %s", a, b);
|
||||
return runwait(cmd) == 0;
|
||||
}
|
||||
|
||||
static int
|
||||
file_has(const char *path, const char *needle)
|
||||
{
|
||||
FILE *f = fopen(path, "rb");
|
||||
if (!f) return 0;
|
||||
static char buf[1 << 20];
|
||||
size_t n = fread(buf, 1, sizeof buf - 1, f);
|
||||
fclose(f);
|
||||
buf[n] = '\0';
|
||||
return strstr(buf, needle) != NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
run_driver(const char *driver, const char *tmpdir, const char *src,
|
||||
const char *label)
|
||||
{
|
||||
char cmd[1024], outbin[256];
|
||||
snprintf(outbin, sizeof outbin, "%s/out_%s", tmpdir, driver);
|
||||
snprintf(cmd, sizeof cmd, "%s/%s build -o %s %s >/dev/null 2>&1",
|
||||
g_bin, driver, outbin, src);
|
||||
if (runwait(cmd) != 0) {
|
||||
fprintf(stderr, "row[%s]: build via %s failed\n",
|
||||
label, driver);
|
||||
return -1;
|
||||
}
|
||||
return runwait(outbin);
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
static char absbin[512];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[256];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
g_bin = bin;
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
const struct row *r = &rows[i];
|
||||
char tmpdir[128], rmcmd[160];
|
||||
char src[160], cs_s[160], ww_s[160];
|
||||
char cs_e[160], ww_e[160];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/tves_%d_d_%d",
|
||||
getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
snprintf(src, sizeof src, "%s/tves_%d_%d.ww",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(cs_s, sizeof cs_s, "%s/tves_%d_%d_cs.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ww_s, sizeof ww_s, "%s/tves_%d_%d_ww.s",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(cs_e, sizeof cs_e, "%s/tves_%d_%d_cs.err",
|
||||
tmpdir, getpid(), i);
|
||||
snprintf(ww_e, sizeof ww_e, "%s/tves_%d_%d_ww.err",
|
||||
tmpdir, getpid(), i);
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return 1; }
|
||||
fputs("package main;\n\n", f);
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
total++;
|
||||
int cs_rc = compile_s("w6c", src, cs_s, cs_e);
|
||||
int ww_rc = compile_s("w6c_ww", src, ww_s, ww_e);
|
||||
if (cs_rc != 0 || ww_rc != 0) {
|
||||
fprintf(stderr, "FAIL row[%s]: compile rc cs=%d "
|
||||
"ww=%d\n", r->label, cs_rc, ww_rc);
|
||||
fail++;
|
||||
runwait(rmcmd);
|
||||
continue;
|
||||
}
|
||||
if (!file_eq(cs_s, ww_s)) {
|
||||
fprintf(stderr, "FAIL row[%s]: cs != ww .s\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
/* the pre-fix undefined-symbol signature: a `(SB)` load of
|
||||
* the singleton type-name. Both unions name the singleton
|
||||
* `too_long`; the wide row also has it. No row legitimately
|
||||
* loads a `too_long(SB)` value (it is a type, not a global). */
|
||||
if (file_has(cs_s, "too_long(SB)")) {
|
||||
fprintf(stderr, "FAIL row[%s]: cstage still loads "
|
||||
"the nonexistent void-singleton payload symbol\n",
|
||||
r->label);
|
||||
fail++;
|
||||
}
|
||||
int got_cs = run_driver("ww", tmpdir, src, r->label);
|
||||
if (got_cs != r->want) {
|
||||
fprintf(stderr, "FAIL row[%s] cstage: want %d "
|
||||
"got %d\n", r->label, r->want, got_cs);
|
||||
fail++;
|
||||
}
|
||||
char wwdrv[600];
|
||||
snprintf(wwdrv, sizeof wwdrv, "%s/ww_ww", g_bin);
|
||||
if (access(wwdrv, X_OK) == 0) {
|
||||
int got_ww = run_driver("ww_ww", tmpdir, src, r->label);
|
||||
if (got_ww != r->want) {
|
||||
fprintf(stderr, "FAIL row[%s] wwstage: "
|
||||
"want %d got %d\n",
|
||||
r->label, r->want, got_ww);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
runwait(rmcmd);
|
||||
}
|
||||
if (fail) {
|
||||
fprintf(stderr, "void_error_singleton_run: %d/%d rows "
|
||||
"failed\n", fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("void_error_singleton_run: %d rows ok\n", total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* 989_globstructwiden_run — F8-c7 (report-item #50, #263): widening a module-
|
||||
* GLOBAL struct ident into a tagged union (`let x: (S | str) = gs;`) must
|
||||
* copy gs's full payload into the box, not just word0.
|
||||
*
|
||||
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages):
|
||||
* the struct-payload widen arm keyed off a local/literal-only struct-name
|
||||
* lookup, so a module-global struct ident fell to the scalar word0 arm — only
|
||||
* the first 8 bytes reached the box (wwstage), or the payload zero-filled
|
||||
* (cstage, gs never read). The ww half landed in F8-c7; the cstage half (a
|
||||
* global-struct-source branch — LEAQ g(SB),SI via aggarg_srcaddr, byte-copy
|
||||
* into slot+8) landed in ww-core TASK #43. Both stages now copy the full
|
||||
* payload and the .s is byte-identical.
|
||||
*
|
||||
* Rows assert the field-equality match returns 0 on both stages (cs==ww).
|
||||
* row | shape | exit
|
||||
* -----------+---------------------------------------------+-----
|
||||
* widen_16 | gp:{a=5,b=6}; (pt|str)=gp; match pt fields | 0
|
||||
* widen_24 | gp:{a=5,b=6,c=7}; (tri|str)=gp; match fields | 0
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want_cs;
|
||||
int want_ww;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "widen_16",
|
||||
"package main;\n"
|
||||
"type pt = struct { a: i64, b: i64 };\n"
|
||||
"let gp: pt = pt { a = 5, b = 6 };\n"
|
||||
"export fn main() int = {\n"
|
||||
" let x: (pt | str) = gp;\n"
|
||||
" match (x) {\n"
|
||||
" case let q: pt => { if (q.a == 5 && q.b == 6) { return 0; }; return 2; };\n"
|
||||
" case str => return 3;\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0, 0 },
|
||||
|
||||
{ "widen_24",
|
||||
"package main;\n"
|
||||
"type tri = struct { a: i64, b: i64, c: i64 };\n"
|
||||
"let gp: tri = tri { a = 5, b = 6, c = 7 };\n"
|
||||
"export fn main() int = {\n"
|
||||
" let x: (tri | str) = gp;\n"
|
||||
" match (x) {\n"
|
||||
" case let q: tri => { if (q.a == 5 && q.b == 6 && q.c == 7) { return 0; }; return 2; };\n"
|
||||
" case str => return 3;\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0, 0 },
|
||||
};
|
||||
|
||||
/* run_build — build+run `src` via `driver`; returns the binary's exit
|
||||
* code, or -1 on a build failure. */
|
||||
static int
|
||||
run_build(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/gsw_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/gsw_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/gsw_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -2; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
driver, outbin, src);
|
||||
int brc = runwait(cmd);
|
||||
|
||||
int got = -1;
|
||||
if (brc == 0) got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return brc == 0 ? got : -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024], wdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *drv; int gated; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
|
||||
fprintf(stderr, "globstructwiden_run: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].drv);
|
||||
continue;
|
||||
}
|
||||
int is_ww = (d == 1);
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int want = is_ww ? rows[i].want_ww : rows[i].want_cs;
|
||||
int got = run_build(drivers[d].drv, &rows[i], i);
|
||||
if (got != want) {
|
||||
fprintf(stderr, "globstructwiden_run[%s][%s]: exit=%d "
|
||||
"want=%d\n", drivers[d].name, rows[i].label,
|
||||
got, want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "globstructwiden_run: %d/%d fixtures failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("globstructwiden_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* 989_globtagisas_run — F8-c3 (report-item #18): `is`/`as` on a module-
|
||||
* GLOBAL tagged-union ident must read the tag/payload from g(SB), not the
|
||||
* saved-BP word.
|
||||
*
|
||||
* THE BUG (cat-A silent miscompile): in cgenexpr.ww cgtypetest (`is`) and
|
||||
* cgtypeassert (`as`) resolve an N_IDENT scrutinee's slot via localfindnode.
|
||||
* A module-global ident returns nil (no BP slot), so scrutoff stayed 0 and
|
||||
* the tag read fell on 0(BP) = the saved-BP word (and the payload on 8(BP) =
|
||||
* the return address). cstage N_TYPETEST is uniformly cgexpr (MOVQ g(SB),AX),
|
||||
* so the bootstrap corpus never trips it on 990-997 — a runtime row is the net.
|
||||
*
|
||||
* MIXED CATEGORY (per-half, asm-verified — spec §3 principle):
|
||||
* • `is` half = ALIGN-UP. cstage reads the tag correctly (MOVQ g(SB),AX);
|
||||
* wwstage read 0(BP). FIX routes the global ident to MOVQ g(SB),AX →
|
||||
* cs==ww + runtime-correct (the is_* rows assert want on BOTH drivers).
|
||||
* • `as` half = #263 BOTH-WRONG, now CLOSED both stages. cstage N_TYPEASSERT
|
||||
* on a global ident spilled cgexpr's AX (tag only) plus UNINITIALIZED DX as
|
||||
* the payload (never loaded g(SB)+8) → garbage (0). The ww half landed in
|
||||
* F8-c3; the cstage half (copy the box words from g(SB)+0/+8[/+16] into the
|
||||
* @asrt_spill so the tag-check + payload load index off memory like a local)
|
||||
* landed in ww-core TASK #46. Both stages now read the real payload and the
|
||||
* .s is byte-identical.
|
||||
*
|
||||
* Rows (built+run on cstage `ww` and wwstage `ww_ww`; cs==ww on every row):
|
||||
* row | shape | exit | cat
|
||||
* ----------+----------------------------------------+------+--------
|
||||
* is_match | g:(i64|bool)=5; if g is i64 →0 else 12 | 0 | align-UP
|
||||
* is_nomatch| g:(i64|bool)=5; if g is bool →13 else 0 | 0 | align-UP
|
||||
* as_i64 | g:(i64|bool)=42; (g as i64):int | 42 | #46
|
||||
* as_str | g:(str|i64)="hello"; len(g as str) | 5 | #46
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want_cs;
|
||||
int want_ww;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "is_match",
|
||||
"package main;\n"
|
||||
"let g: (i64 | bool) = 5;\n"
|
||||
"fn check() int = { if (g is i64) { return 0; }; return 12; };\n"
|
||||
"export fn main() int = { return check(); };\n",
|
||||
0, 0 },
|
||||
|
||||
{ "is_nomatch",
|
||||
"package main;\n"
|
||||
"let g: (i64 | bool) = 5;\n"
|
||||
"fn check() int = { if (g is bool) { return 13; }; return 0; };\n"
|
||||
"export fn main() int = { return check(); };\n",
|
||||
0, 0 },
|
||||
|
||||
/* #46 CLOSED both stages: cstage now copies the box from g(SB) into the
|
||||
* @asrt_spill (was spilling uninitialized DX) → 42 == wwstage. */
|
||||
{ "as_i64",
|
||||
"package main;\n"
|
||||
"let g: (i64 | bool) = 42;\n"
|
||||
"fn check() int = { let x: i64 = g as i64; return x: int; };\n"
|
||||
"export fn main() int = { return check(); };\n",
|
||||
42, 42 },
|
||||
|
||||
/* #46 str variant — both stages copy the +16 cap word too. */
|
||||
{ "as_str",
|
||||
"package main;\n"
|
||||
"let g: (str | i64) = \"hello\";\n"
|
||||
"fn check() int = { let s: str = g as str; return len(s): int; };\n"
|
||||
"export fn main() int = { return check(); };\n",
|
||||
5, 5 },
|
||||
};
|
||||
|
||||
/* run_build — build+run `src` via `driver`; returns the binary's exit
|
||||
* code, or -1 on a build failure. */
|
||||
static int
|
||||
run_build(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/gtia_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/gtia_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/gtia_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -2; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
driver, outbin, src);
|
||||
int brc = runwait(cmd);
|
||||
|
||||
int got = -1;
|
||||
if (brc == 0) got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return brc == 0 ? got : -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024], wdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *drv; int gated; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
|
||||
fprintf(stderr, "globtagisas_run: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].drv);
|
||||
continue;
|
||||
}
|
||||
int is_ww = (d == 1);
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int want = is_ww ? rows[i].want_ww : rows[i].want_cs;
|
||||
int got = run_build(drivers[d].drv, &rows[i], i);
|
||||
if (got != want) {
|
||||
fprintf(stderr, "globtagisas_run[%s][%s]: exit=%d "
|
||||
"want=%d\n", drivers[d].name, rows[i].label,
|
||||
got, want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "globtagisas_run: %d/%d fixtures failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("globtagisas_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* 989_globtagreassign_run — F8-c5 (report-item #32, #263): reassigning a
|
||||
* module-GLOBAL tagged-union ident (`g = expr`) must store the new tag and
|
||||
* payload, not clobber only one word.
|
||||
*
|
||||
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages):
|
||||
* cgassign's tagged-union reassignment arm delegated to the widener ONLY for a
|
||||
* LOCAL ident. A module-global tagged ident had no arm and fell through to the
|
||||
* generic scalar store (wwstage clobbered the tag word; cstage dropped the
|
||||
* store entirely). The ww half landed in F8-c5; the cstage half (LEAQ g(SB),BX
|
||||
* then cg_widen_tagged_store off BX) landed in ww-core TASK #41 — both stages
|
||||
* now store the full box and the .s is byte-identical.
|
||||
*
|
||||
* Rows (cs==ww after the cstage half landed):
|
||||
* row | shape | exit
|
||||
* ----------------+----------------------------------------+-----
|
||||
* reassign_i64 | g:(i64|void)=0; g=7; match i64 | 7
|
||||
* reassign_str | g:(str|i64)=0; g="hello"; match str→len | 5
|
||||
* reassign_union | g:(i64|bool)=0; g=5; match i64 | 5
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want_cs;
|
||||
int want_ww;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "reassign_i64",
|
||||
"package main;\n"
|
||||
"let g: (i64 | void) = 0;\n"
|
||||
"export fn main() int = {\n"
|
||||
" g = 7;\n"
|
||||
" match (g) {\n"
|
||||
" case let n: i64 => return n: int;\n"
|
||||
" case void => return 99;\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
7, 7 },
|
||||
|
||||
{ "reassign_str",
|
||||
"package main;\n"
|
||||
"let g: (str | i64) = 0;\n"
|
||||
"export fn main() int = {\n"
|
||||
" g = \"hello\";\n"
|
||||
" match (g) {\n"
|
||||
" case let s: str => return len(s): int;\n"
|
||||
" case i64 => return 88;\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
5, 5 },
|
||||
|
||||
{ "reassign_union",
|
||||
"package main;\n"
|
||||
"let g: (i64 | bool) = 0;\n"
|
||||
"export fn main() int = {\n"
|
||||
" g = 5;\n"
|
||||
" match (g) {\n"
|
||||
" case let n: i64 => return n: int;\n"
|
||||
" case bool => return 77;\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
5, 5 },
|
||||
};
|
||||
|
||||
/* run_build — build+run `src` via `driver`; returns the binary's exit
|
||||
* code, or -1 on a build failure. */
|
||||
static int
|
||||
run_build(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/gtr_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/gtr_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/gtr_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -2; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
driver, outbin, src);
|
||||
int brc = runwait(cmd);
|
||||
|
||||
int got = -1;
|
||||
if (brc == 0) got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return brc == 0 ? got : -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024], wdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *drv; int gated; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
|
||||
fprintf(stderr, "globtagreassign_run: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].drv);
|
||||
continue;
|
||||
}
|
||||
int is_ww = (d == 1);
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int want = is_ww ? rows[i].want_ww : rows[i].want_cs;
|
||||
int got = run_build(drivers[d].drv, &rows[i], i);
|
||||
if (got != want) {
|
||||
fprintf(stderr, "globtagreassign_run[%s][%s]: exit=%d "
|
||||
"want=%d\n", drivers[d].name, rows[i].label,
|
||||
got, want);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "globtagreassign_run: %d/%d fixtures failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("globtagreassign_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
/*
|
||||
* 989_globtagwiden_run — F8-c8 (report-item #51, #263): widening a module-
|
||||
* GLOBAL tagged-union ident into a wider union (`let x: (inner | str) = gi;`)
|
||||
* must copy gi's whole box as the nested payload, not store its tag as the
|
||||
* payload.
|
||||
*
|
||||
* THE BUG (cat-A silent miscompile, #263 BOTH-WRONG, now CLOSED both stages):
|
||||
* the #218 nested-tagged-source arm was gated local-only, so a module-global
|
||||
* tagged source fell to the scalar word0 widen arm — gi's TAG word landed as
|
||||
* the box payload (wwstage), or frame garbage was copied, never gi(SB)
|
||||
* (cstage). The ww half landed in F8-c8; the cstage half (the nested arm's
|
||||
* N_IDENT copy grows a global branch — LEAQ gi(SB),SI via aggarg_srcaddr, copy
|
||||
* the inner box into slot+8) landed in ww-core TASK #44. Both stages now copy
|
||||
* the whole box and the .s is byte-identical.
|
||||
*
|
||||
* Rows assert the exact runtime-correct value on both stages (cs==ww).
|
||||
* row | shape | exit
|
||||
* ----------+-----------------------------------------+-----
|
||||
* nest_47 | gi:inner=47; (inner|str)=gi; match→v | 47
|
||||
* nest_99 | gi:inner=99; (inner|str)=gi; match→v | 99
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want_ww;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
{ "nest_47",
|
||||
"package main;\n"
|
||||
"type inner = (i64 | bool);\n"
|
||||
"let gi: inner = 47;\n"
|
||||
"export fn main() int = {\n"
|
||||
" let x: (inner | str) = gi;\n"
|
||||
" match (x) {\n"
|
||||
" case let n: inner => {\n"
|
||||
" match (n) {\n"
|
||||
" case let v: i64 => return v: int;\n"
|
||||
" case bool => return 88;\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
" case str => return 3;\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
47 },
|
||||
|
||||
{ "nest_99",
|
||||
"package main;\n"
|
||||
"type inner = (i64 | bool);\n"
|
||||
"let gi: inner = 99;\n"
|
||||
"export fn main() int = {\n"
|
||||
" let x: (inner | str) = gi;\n"
|
||||
" match (x) {\n"
|
||||
" case let n: inner => {\n"
|
||||
" match (n) {\n"
|
||||
" case let v: i64 => return v: int;\n"
|
||||
" case bool => return 88;\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
" case str => return 3;\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
99 },
|
||||
};
|
||||
|
||||
/* run_build — build+run `src` via `driver`; returns the binary's exit
|
||||
* code, or -1 on a build failure. */
|
||||
static int
|
||||
run_build(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/gtw_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/gtw_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/gtw_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -2; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
driver, outbin, src);
|
||||
int brc = runwait(cmd);
|
||||
|
||||
int got = -1;
|
||||
if (brc == 0) got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return brc == 0 ? got : -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024], wdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *drv; int gated; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
|
||||
fprintf(stderr, "globtagwiden_run: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].drv);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int got = run_build(drivers[d].drv, &rows[i], i);
|
||||
/* #44 CLOSED: cs==ww at the exact runtime-correct value
|
||||
* on both drivers (the .s is byte-identical). */
|
||||
if (got != rows[i].want_ww) {
|
||||
fprintf(stderr, "globtagwiden_run[%s][%s]:"
|
||||
" exit=%d want=%d\n", drivers[d].name,
|
||||
rows[i].label, got, rows[i].want_ww);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "globtagwiden_run: %d/%d fixtures failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("globtagwiden_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* 989_taggedderefstore_run — #17 teeth (recorded by impl-write, 2026-06-13).
|
||||
*
|
||||
* Deref-target store into a tagged-union pointee: `*p = v` where p:*tagged.
|
||||
* The plain-deref store arm sized the write off the pointee type and emitted
|
||||
* a SINGLE fldstoreop (one MOVQ) — rhs landed in the tag word, the payload
|
||||
* was dropped, and the union's discriminant was corrupted. A subsequent
|
||||
* `match (v)` then dispatched on garbage.
|
||||
*
|
||||
* THIS WAS BOTH-STAGE-WRONG AND BYTE-ID-GREEN (#263-class, gate-blind): the
|
||||
* cstage (cmd/w6c/cgen.c #17 deref else-branch) and wwstage (cgenexpr.ww
|
||||
* #17 single-store tail) emitted IDENTICAL wrong asm, so the byte-id gates
|
||||
* never caught it. #17 routes both stages' tagged-pointee deref store
|
||||
* through the widener (cstage cg_widen_tagged_store / ww cgwidentaggedstore)
|
||||
* via the shared tag scratch, then word-copies scratch -> *p — mirroring the
|
||||
* proven index-element tagged arm (cstage cgen.c:6487 / ww cgenexpr.ww:8768).
|
||||
* Scalar pointees keep the single-store path untouched.
|
||||
*
|
||||
* Rows (each program self-checks and returns 0 on all-correct, a 1-based
|
||||
* code on the first mismatch; both stages build+run, rule-10, pin 0):
|
||||
* taglo: *(int|bool), *p=42 — tag-low (int) variant, read back 42.
|
||||
* taghi: *(int|bool), *p=true — tag-high (bool) variant survives.
|
||||
* str3w: *(int|str), *p="hello" — a str payload is a 3-word {ptr,len,
|
||||
* cap} header (slot = tag + 3 payload words); proves the
|
||||
* multi-payload-word copy is COMPLETE, not just word0/word1.
|
||||
* struct2w: *(int|pair), *p=pair{...} — a 16B struct payload (slot = tag +
|
||||
* 2 payload words, ssz 24); proves the mid-count copy loop moves
|
||||
* BOTH payload words and the widener's struct-source arm fires.
|
||||
* nullfold: *(*int|void), *p=&a — nullable fold (1-word slot, pointer IS
|
||||
* the disc); the folded payload path widens correctly.
|
||||
* scalarctl:*int, *p=42 — scalar control: the non-tagged single-store
|
||||
* path is UNCHANGED (no scratch, no widen).
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want_exit; /* >= 0: pin the absolute value; -1: cs==ww only */
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* taglo — tag-low (int) variant via *p=42; read back the int. */
|
||||
{ "taglo",
|
||||
"package main;\n"
|
||||
"export fn main() int = {\n"
|
||||
" let v: (int | bool) = false;\n"
|
||||
" let p: *(int | bool) = &v;\n"
|
||||
" *p = 42;\n"
|
||||
" match (v) {\n"
|
||||
" case let n: int => { if (n != 42) { return 1; }; return 0; };\n"
|
||||
" case bool => { return 2; };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* taghi — tag-high (bool) variant via *p=true; the tag must remap. */
|
||||
{ "taghi",
|
||||
"package main;\n"
|
||||
"export fn main() int = {\n"
|
||||
" let v: (int | bool) = 0;\n"
|
||||
" let p: *(int | bool) = &v;\n"
|
||||
" *p = true;\n"
|
||||
" match (v) {\n"
|
||||
" case int => { return 1; };\n"
|
||||
" case let b: bool => { if (!b) { return 2; }; return 0; };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* str3w — str payload (3-word header); the >2-word payload copy must
|
||||
* be complete. Pre-fix only the tag word (or one payload word) moved. */
|
||||
{ "str3w",
|
||||
"package main;\n"
|
||||
"export fn main() int = {\n"
|
||||
" let v: (int | str) = 0;\n"
|
||||
" let p: *(int | str) = &v;\n"
|
||||
" *p = \"hello\";\n"
|
||||
" match (v) {\n"
|
||||
" case int => { return 1; };\n"
|
||||
" case let s: str => { if (s.len: int != 5) { return 2; }; return 0; };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* struct2w — an aggregate (struct) payload: a 16B pair → slot = tag +
|
||||
* 2 payload words (ssz 24, 3-word copy loop). Exercises the widener's
|
||||
* struct-source arm through the deref path and proves the mid-count
|
||||
* copy loop moves BOTH payload words, not word0 alone. */
|
||||
{ "struct2w",
|
||||
"package main;\n"
|
||||
"type pair = struct { a: i64, b: i64 };\n"
|
||||
"export fn main() int = {\n"
|
||||
" let v: (int | pair) = 0;\n"
|
||||
" let p: *(int | pair) = &v;\n"
|
||||
" *p = pair { a = 0x1111, b = 0x2222 };\n"
|
||||
" match (v) {\n"
|
||||
" case int => { return 1; };\n"
|
||||
" case let q: pair => {\n"
|
||||
" if (q.a != 0x1111) { return 2; };\n"
|
||||
" if (q.b != 0x2222) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* nullfold — nullable fold (1-word slot): the pointer is the disc. */
|
||||
{ "nullfold",
|
||||
"package main;\n"
|
||||
"export fn main() int = {\n"
|
||||
" let a: int = 7;\n"
|
||||
" let v: (*int | void) = void;\n"
|
||||
" let p: *(*int | void) = &v;\n"
|
||||
" *p = &a;\n"
|
||||
" match (v) {\n"
|
||||
" case let q: *int => { if (*q != 7) { return 1; }; return 0; };\n"
|
||||
" case void => { return 2; };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* scalarctl — non-tagged pointee: the single-store path is unchanged. */
|
||||
{ "scalarctl",
|
||||
"package main;\n"
|
||||
"export fn main() int = {\n"
|
||||
" let v: int = 3;\n"
|
||||
" let p: *int = &v;\n"
|
||||
" *p = 42;\n"
|
||||
" if (v != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
};
|
||||
|
||||
/* run_build — build+run `src` via `driver`; returns the binary's exit
|
||||
* code, or -1 on a build failure. */
|
||||
static int
|
||||
run_build(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[64], src[128], outbin[128], cmd[1024], rmcmd[160];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/tagderef_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/tagderef_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/tagderef_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -2; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
driver, outbin, src);
|
||||
int brc = runwait(cmd);
|
||||
|
||||
int got = -1;
|
||||
if (brc == 0) got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return brc == 0 ? got : -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024], wdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
int have_ww = (access(wdrv, X_OK) == 0);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int gc = run_build(cdrv, &rows[i], i);
|
||||
if (gc < 0) {
|
||||
fprintf(stderr, "taggedderefstore_run[cstage][%s]: build/run "
|
||||
"failed (got %d)\n", rows[i].label, gc);
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
if (rows[i].want_exit >= 0 && gc != rows[i].want_exit) {
|
||||
fprintf(stderr, "taggedderefstore_run[cstage][%s]: exit=%d "
|
||||
"want=%d (tag corrupt / payload dropped)\n",
|
||||
rows[i].label, gc, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
if (!have_ww) {
|
||||
fprintf(stderr, "taggedderefstore_run: skip wwstage (no %s)\n",
|
||||
wdrv);
|
||||
continue;
|
||||
}
|
||||
int gw = run_build(wdrv, &rows[i], i);
|
||||
if (gw != gc) {
|
||||
fprintf(stderr, "taggedderefstore_run[%s]: cs=%d != ww=%d "
|
||||
"(#17 deref-store divergence)\n",
|
||||
rows[i].label, gc, gw);
|
||||
fail++;
|
||||
}
|
||||
if (rows[i].want_exit >= 0 && gw != rows[i].want_exit) {
|
||||
fprintf(stderr, "taggedderefstore_run[wwstage][%s]: exit=%d "
|
||||
"want=%d (tag corrupt / payload dropped)\n",
|
||||
rows[i].label, gw, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "taggedderefstore_run: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("taggedderefstore_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,248 +0,0 @@
|
||||
/*
|
||||
* 989_taggedglobalindex_run — #16 teeth (recorded by impl-16, 2026-06-14).
|
||||
*
|
||||
* Indexed store into a GLOBAL array of a tagged-union type: `gs[i] = v` where
|
||||
* gs : [N](A | B). The indexed-tagged-element assign arm (cmd/w6c/cgen.c
|
||||
* ~6502) computed its base address with only LOCAL/frame variants — it LACKED
|
||||
* the `isglobal -> LEAQ name(SB)` branch the SCALAR `arr[i] = v` element arm
|
||||
* (~6767) already had. For a global tagged array the base resolved to a junk
|
||||
* BP-relative offset (off==0), so the widened store landed nowhere observable;
|
||||
* a later `match (gs[i])` then read the unchanged static-init slot. cstage
|
||||
* silently returned 0 (WRONG); wwstage (cgenexpr.ww index arm, already carries
|
||||
* the globalname branch) returned the correct value — a cs!=ww divergence the
|
||||
* byte-id gate could SEE. #16 aligns cstage UP: it adds the mirrored
|
||||
* `isglobal && is_arr -> LEAQ name(SB)` / `isglobal -> MOVQ name(SB)` branch to
|
||||
* the indexed-tagged-elem arm. cstage-only; no .ww source touched.
|
||||
*
|
||||
* Rows (each program self-checks and returns 0 on all-correct, a 1-based code
|
||||
* on the first mismatch; both stages build+run, rule-10, pin 0):
|
||||
* glo_taglo: global [N](int|bool), gs[1]=42 — tag-low (int) variant, read
|
||||
* back 42. The literal #16 teeth (cstage pre-fix gave 0).
|
||||
* glo_taghi: global [N](int|bool), gs[1]=true — tag-high (bool) variant;
|
||||
* the tag must remap through the widener.
|
||||
* glo_varidx: global [N](int|bool), runtime index gs[i]=99 — the scaled-
|
||||
* index path (IMULQ) layered on the global base.
|
||||
* glo_str3w: global [N](int|str), gs[1]="hello" — a str payload is a
|
||||
* 3-word {ptr,len,cap} header; proves the multi-word slot copy
|
||||
* over the global base is COMPLETE, not just word0/word1.
|
||||
* glo_struct2w:global [N](int|pair), gs[1]=pair{...} — a 16B struct payload
|
||||
* (slot = tag + 2 payload words); proves the mid-count copy loop
|
||||
* moves BOTH payload words over the global base.
|
||||
* loc_ctl: LOCAL [N](int|bool), a[1]=42 — control for the frame-base
|
||||
* path that ALREADY worked; must stay correct + byte-id.
|
||||
* scalar_ctl: scalar global [N]int, arr[1]=42 — control for the template
|
||||
* arm (the scalar element store) the fix was mirrored from.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want_exit; /* >= 0: pin the absolute value; -1: cs==ww only */
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* glo_taglo — the #16 teeth: global tagged array, int variant store. */
|
||||
{ "glo_taglo",
|
||||
"package main;\n"
|
||||
"let gs: [2](int | bool) = [0, 0];\n"
|
||||
"export fn main() int = {\n"
|
||||
" gs[1] = 42;\n"
|
||||
" match (gs[1]) {\n"
|
||||
" case let n: int => { if (n != 42) { return 1; }; return 0; };\n"
|
||||
" case bool => { return 2; };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* glo_taghi — bool variant via gs[1]=true; the tag must remap. */
|
||||
{ "glo_taghi",
|
||||
"package main;\n"
|
||||
"let gs: [2](int | bool) = [0, 0];\n"
|
||||
"export fn main() int = {\n"
|
||||
" gs[1] = true;\n"
|
||||
" match (gs[1]) {\n"
|
||||
" case int => { return 1; };\n"
|
||||
" case let b: bool => { if (!b) { return 2; }; return 0; };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* glo_varidx — runtime index over the global base (scaled IMULQ). */
|
||||
{ "glo_varidx",
|
||||
"package main;\n"
|
||||
"let gs: [3](int | bool) = [0, 0, 0];\n"
|
||||
"export fn main() int = {\n"
|
||||
" let i: int = 2;\n"
|
||||
" gs[i] = 99;\n"
|
||||
" match (gs[2]) {\n"
|
||||
" case let n: int => { if (n != 99) { return 1; }; return 0; };\n"
|
||||
" case bool => { return 2; };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* glo_str3w — str payload (3-word header); the multi-word slot copy
|
||||
* over the global base must be complete. */
|
||||
{ "glo_str3w",
|
||||
"package main;\n"
|
||||
"let gs: [2](int | str) = [0, 0];\n"
|
||||
"export fn main() int = {\n"
|
||||
" gs[1] = \"hello\";\n"
|
||||
" match (gs[1]) {\n"
|
||||
" case int => { return 1; };\n"
|
||||
" case let s: str => { if (s.len: int != 5) { return 2; }; return 0; };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* glo_struct2w — a 16B struct payload (slot = tag + 2 payload words);
|
||||
* proves the mid-count copy loop moves BOTH payload words. */
|
||||
{ "glo_struct2w",
|
||||
"package main;\n"
|
||||
"type pair = struct { a: i64, b: i64 };\n"
|
||||
"let gs: [2](int | pair) = [0, 0];\n"
|
||||
"export fn main() int = {\n"
|
||||
" gs[1] = pair { a = 0x1111, b = 0x2222 };\n"
|
||||
" match (gs[1]) {\n"
|
||||
" case int => { return 1; };\n"
|
||||
" case let q: pair => {\n"
|
||||
" if (q.a != 0x1111) { return 2; };\n"
|
||||
" if (q.b != 0x2222) { return 3; };\n"
|
||||
" return 0;\n"
|
||||
" };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* loc_ctl — LOCAL tagged array: the frame-base path that already worked
|
||||
* must stay correct (the fix must not perturb the non-global branch). */
|
||||
{ "loc_ctl",
|
||||
"package main;\n"
|
||||
"export fn main() int = {\n"
|
||||
" let a: [2](int | bool) = [0, 0];\n"
|
||||
" a[1] = 42;\n"
|
||||
" match (a[1]) {\n"
|
||||
" case let n: int => { if (n != 42) { return 1; }; return 0; };\n"
|
||||
" case bool => { return 2; };\n"
|
||||
" };\n"
|
||||
"};\n",
|
||||
0 },
|
||||
|
||||
/* scalar_ctl — scalar global array: the template arm the fix mirrors. */
|
||||
{ "scalar_ctl",
|
||||
"package main;\n"
|
||||
"let arr: [3]int = [0, 0, 0];\n"
|
||||
"export fn main() int = {\n"
|
||||
" arr[1] = 42;\n"
|
||||
" if (arr[1] != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n",
|
||||
0 },
|
||||
};
|
||||
|
||||
/* run_build — build+run `src` via `driver`; returns the binary's exit code,
|
||||
* or -1 on a build failure. */
|
||||
static int
|
||||
run_build(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[64], src[128], outbin[128], cmd[1024], rmcmd[160];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/tagglobidx_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/tagglobidx_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/tagglobidx_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -2; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
driver, outbin, src);
|
||||
int brc = runwait(cmd);
|
||||
|
||||
int got = -1;
|
||||
if (brc == 0) got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return brc == 0 ? got : -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024], wdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
int have_ww = (access(wdrv, X_OK) == 0);
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int gc = run_build(cdrv, &rows[i], i);
|
||||
if (gc < 0) {
|
||||
fprintf(stderr, "taggedglobalindex_run[cstage][%s]: build/run "
|
||||
"failed (got %d)\n", rows[i].label, gc);
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
if (rows[i].want_exit >= 0 && gc != rows[i].want_exit) {
|
||||
fprintf(stderr, "taggedglobalindex_run[cstage][%s]: exit=%d "
|
||||
"want=%d (global base dropped / store lost)\n",
|
||||
rows[i].label, gc, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
if (!have_ww) {
|
||||
fprintf(stderr, "taggedglobalindex_run: skip wwstage (no %s)\n",
|
||||
wdrv);
|
||||
continue;
|
||||
}
|
||||
int gw = run_build(wdrv, &rows[i], i);
|
||||
if (gw != gc) {
|
||||
fprintf(stderr, "taggedglobalindex_run[%s]: cs=%d != ww=%d "
|
||||
"(#16 indexed-global-tagged base divergence)\n",
|
||||
rows[i].label, gc, gw);
|
||||
fail++;
|
||||
}
|
||||
if (rows[i].want_exit >= 0 && gw != rows[i].want_exit) {
|
||||
fprintf(stderr, "taggedglobalindex_run[wwstage][%s]: exit=%d "
|
||||
"want=%d (global base dropped / store lost)\n",
|
||||
rows[i].label, gw, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "taggedglobalindex_run: %d/%d checks failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("taggedglobalindex_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,191 +0,0 @@
|
||||
/*
|
||||
* 989_taggedidx_run — F7-c6 (#23): a tagged-union element indexed off a
|
||||
* CALL or SLICE base must load the full tag+payload cursor, both stages.
|
||||
*
|
||||
* THE BUG (cat-A silent miscompile, gate-blind): cgindex
|
||||
* (selfhost/cmd/wcc/cgenexpr.ww) classifies an indexed element as tagged
|
||||
* so it loads the (tag, val0, val1) slot. The non-ident-base arm gated
|
||||
* that classify on a base-kind WHITELIST (N_DOT / N_INDEX / N_UN-deref),
|
||||
* so a tagged element indexed off an N_CALL base (`f()[i]`) or N_SLICE
|
||||
* base (`s[a:b][i]`) fell through to the scalar load — a lone
|
||||
* `MOVQ (AX),AX` (tag word only), dropping the payload. cstage classifies
|
||||
* for ANY base off the stamped element tinfo (`esubu->kind == TY_TAGGED`,
|
||||
* cmd/w6c/cgen.c:8101), so it loaded the full cursor and ran correct
|
||||
* (cs rc=50 / ww rc=40 in the review repro — the cat-A divergence).
|
||||
* 990-997 stay green because the corpus never indexes a tagged element
|
||||
* off a call/slice base; only a runtime row catches it. THE FIX: drop the
|
||||
* base-kind whitelist — classify the tagged element off n.type_ for any
|
||||
* non-N_IDENT base, aligning wwstage UP.
|
||||
*
|
||||
* Arrays of tagged are built by per-element store; box = (i64|bool).
|
||||
*
|
||||
* Rows (cstage `ww` + gated wwstage `ww_ww`; rule-10 + absolute value):
|
||||
* row | shape | want
|
||||
* -----------------+--------------------------------+------
|
||||
* slice_tagged_idx | arr[1:][0] (N_SLICE base) | 40 [#23 bug]
|
||||
* call_tagged_idx | getrows()[1] (N_CALL base) | 50 [#23 bug]
|
||||
* ident_tagged_idx | arr[1] (N_IDENT base) | 40 (control: the
|
||||
* | first arm already handled it — no regression)
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want_exit;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* (1) #23 — tagged element off a SLICE base `arr[1:][0]`. arr[1] is the
|
||||
* i64 variant 40; the bug loaded only the tag word → garbage match. */
|
||||
{ "slice_tagged_idx",
|
||||
"package main;\n"
|
||||
"type box = (i64 | bool);\n"
|
||||
"export fn main() int = {\n"
|
||||
" let arr: [3]box;\n"
|
||||
" arr[0] = 10; arr[1] = 40; arr[2] = 70;\n"
|
||||
" let v: box = arr[1:][0];\n"
|
||||
" let r: i64 = 0;\n"
|
||||
" match (v) {\n"
|
||||
" case let x: i64 => { r = x; };\n"
|
||||
" case let b: bool => { r = 99; };\n"
|
||||
" };\n"
|
||||
" return r: int;\n"
|
||||
"};\n",
|
||||
40 },
|
||||
|
||||
/* (2) #23 — tagged element off a CALL base `getrows()[1]`. The fn
|
||||
* returns []box; element 1 is the i64 variant 50. */
|
||||
{ "call_tagged_idx",
|
||||
"package main;\n"
|
||||
"type box = (i64 | bool);\n"
|
||||
"fn getrows() []box = {\n"
|
||||
" let arr: [3]box;\n"
|
||||
" arr[0] = 10; arr[1] = 50; arr[2] = 70;\n"
|
||||
" return arr[0:];\n"
|
||||
"};\n"
|
||||
"export fn main() int = {\n"
|
||||
" let v: box = getrows()[1];\n"
|
||||
" let r: i64 = 0;\n"
|
||||
" match (v) {\n"
|
||||
" case let x: i64 => { r = x; };\n"
|
||||
" case let b: bool => { r = 99; };\n"
|
||||
" };\n"
|
||||
" return r: int;\n"
|
||||
"};\n",
|
||||
50 },
|
||||
|
||||
/* (3) control — tagged element off an N_IDENT base `arr[1]` (the first
|
||||
* cgindex arm already handled it via istaggedtype on the base tnode):
|
||||
* c6 must not regress it. arr[1] == 40. */
|
||||
{ "ident_tagged_idx",
|
||||
"package main;\n"
|
||||
"type box = (i64 | bool);\n"
|
||||
"export fn main() int = {\n"
|
||||
" let arr: [3]box;\n"
|
||||
" arr[0] = 10; arr[1] = 40; arr[2] = 70;\n"
|
||||
" let v: box = arr[1];\n"
|
||||
" let r: i64 = 0;\n"
|
||||
" match (v) {\n"
|
||||
" case let x: i64 => { r = x; };\n"
|
||||
" case let b: bool => { r = 99; };\n"
|
||||
" };\n"
|
||||
" return r: int;\n"
|
||||
"};\n",
|
||||
40 },
|
||||
};
|
||||
|
||||
/* run_build — build+run `src` via `driver`; returns the binary's exit
|
||||
* code, or -1 on a build failure. */
|
||||
static int
|
||||
run_build(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char tmpdir[64], src[128], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/tagidx_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/tagidx_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/tagidx_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -2; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
driver, outbin, src);
|
||||
int brc = runwait(cmd);
|
||||
|
||||
int got = -1;
|
||||
if (brc == 0) got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return brc == 0 ? got : -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024], wdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *drv; int gated; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
|
||||
fprintf(stderr, "taggedidx_run: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].drv);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int got = run_build(drivers[d].drv, &rows[i], i);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "taggedidx_run[%s][%s]: exit=%d "
|
||||
"want=%d\n", drivers[d].name, rows[i].label,
|
||||
got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "taggedidx_run: %d/%d fixtures failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("taggedidx_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
@@ -1,265 +0,0 @@
|
||||
/*
|
||||
* 989_tagnorm_run — F1 (#1/#3): tagged-union type-set normalization must be
|
||||
* symmetric across stages. cstage resolve_type N_TTAGGED (cmd/wcc/check.c:
|
||||
* 801-882) drops `never`, dedups variants (variant_match: NAMED nominal,
|
||||
* else structural), collapses a lone survivor to that variant, and folds a
|
||||
* two-variant `(*T|void)` to an 8B nullable pointer. The wwstage formerly
|
||||
* skipped ALL four at BOTH size sources — astsize (selfhost/cmd/wcc/check.ww)
|
||||
* and tinfofornode N_TTAGGED — so size()/align() folded the RAW declaration
|
||||
* list and tag numbering ran off it too.
|
||||
*
|
||||
* THE BUG (cat-A silent miscompile, gate-blind): size((*u8|void)) folded
|
||||
* 16 in ww vs 8 in cs; size((i32|never)) 16 vs 4; align((i32|never)) 8 vs 4;
|
||||
* size((i32|i32)) 16 vs 4 — all rc=0 both stages, divergent constant baked
|
||||
* into the .s (MOVQ $16 vs $8/$4). The corpus never declares a never/dup/
|
||||
* collapsible/nullable union whose SIZE it folds, so 990-997 stayed green;
|
||||
* only a runtime size-fold row catches it.
|
||||
*
|
||||
* THE FIX: ONE normalization site — tinfofornode N_TTAGGED (the tinfo SSoT
|
||||
* cgen already reads via ti.params/ti.size) gained never-drop + dedup +
|
||||
* single-collapse; astsize/astalign N_TTAGGED delegate to it (mirroring the
|
||||
* existing N_TTUPLE arm). cgen's deduped tag numbering rides ti.params for
|
||||
* free.
|
||||
*
|
||||
* Rows (cstage `ww` + gated wwstage `ww_ww`; rule-10 + absolute value):
|
||||
* row | shape | want | pre-fix ww
|
||||
* ---------------+-------------------------------+------+-----------
|
||||
* nullable_size | size((*u8|void)) | 8 | 16 [#1]
|
||||
* never_size | size((i32|never)) | 4 | 16 [#1]
|
||||
* dup_size | size((i32|i32)) | 4 | 16 [#1]
|
||||
* never_align | align((i32|never)) | 4 | 8 [#1]
|
||||
* dedup_match | "hi": (i32|i32|str), match str | 7 | 7 (green
|
||||
* | both — dedup keeps construct/match consistent; the
|
||||
* | tag-NUMBER divergence #3 is byte-id, caught by B1)
|
||||
* void_dedup_ret | bare `return;` of (i32|i32|void)| 5 | !5 [voidvar-
|
||||
* | then match the void variant | | iantindex fold]
|
||||
* void_pos0_ret | bare `return;` of (void|i32) | 5 | 5 (idx-0
|
||||
* | | | boundary)
|
||||
* void_pos2_ret | bare `return;` of (i32|str|void)| 5 | !5 [idx-2
|
||||
* | | | teeth]
|
||||
*
|
||||
* void_{dedup,pos0,pos2}_ret pin voidvariantindex's idx counter at the
|
||||
* three normalized positions (0/1/2): the first fold omitted `idx += 1`,
|
||||
* so it returned 0 for ANY void position — only positions 1 and 2 are RED
|
||||
* under that bug (position 0 is the one value it happened to get right, a
|
||||
* boundary pin). void_dedup_ret is the gate-blind teeth for the cgenutil
|
||||
* voidvariantindex
|
||||
* fold: the bare-`return;` void tag must come off the NORMALIZED (deduped)
|
||||
* variant chain, like construct/match already do. With the F1 checker dedup
|
||||
* (i32|i32|void)->(i32|void) but a RAW-list voidvariantindex, bare return
|
||||
* stores tag 2 while match expects void at tag 1 -> no arm fires, r stays 0
|
||||
* (RED). The fold reads ti.params -> tag 1 -> the void arm yields 5.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
static int
|
||||
runwait(const char *cmd)
|
||||
{
|
||||
int rc = system(cmd);
|
||||
if (rc == -1) return -1;
|
||||
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct row {
|
||||
const char *label;
|
||||
const char *src;
|
||||
int want_exit;
|
||||
};
|
||||
|
||||
static const struct row rows[] = {
|
||||
/* (1) #1 — nullable `(*T|void)` folds to an 8B pointer slot, not a
|
||||
* 16B tag+payload box. */
|
||||
{ "nullable_size",
|
||||
"package main;\n"
|
||||
"type p = (*u8 | void);\n"
|
||||
"export fn main() int = { return size(p): int; };\n",
|
||||
8 },
|
||||
|
||||
/* (2) #1 — `never` drops, leaving a lone i32 → the union collapses to
|
||||
* i32 (size 4), not an 8+8 box. */
|
||||
{ "never_size",
|
||||
"package main;\n"
|
||||
"type q = (i32 | never);\n"
|
||||
"export fn main() int = { return size(q): int; };\n",
|
||||
4 },
|
||||
|
||||
/* (3) #1 — duplicate variants dedup to one i32 → collapse to i32. */
|
||||
{ "dup_size",
|
||||
"package main;\n"
|
||||
"type d = (i32 | i32);\n"
|
||||
"export fn main() int = { return size(d): int; };\n",
|
||||
4 },
|
||||
|
||||
/* (4) #1 — align() rides the same normalization: the collapsed i32
|
||||
* aligns to 4, not the tag word's 8. */
|
||||
{ "never_align",
|
||||
"package main;\n"
|
||||
"type q = (i32 | never);\n"
|
||||
"export fn main() int = { return align(q): int; };\n",
|
||||
4 },
|
||||
|
||||
/* (5) #3 — a deduped `(i32|i32|str)` still constructs+matches its str
|
||||
* variant correctly in BOTH stages (the str-arm yields 7). Pins
|
||||
* cs==ww on the functional outcome; the per-stage tag-NUMBER (str=$1
|
||||
* vs $2) divergence is rule-10 byte-id, proven by B1 self-compile. */
|
||||
{ "dedup_match",
|
||||
"package main;\n"
|
||||
"type u = (i32 | i32 | str);\n"
|
||||
"export fn main() int = {\n"
|
||||
" let v: u = \"hi\";\n"
|
||||
" let r: i64 = 0;\n"
|
||||
" match (v) {\n"
|
||||
" case let s: str => { r = 7; };\n"
|
||||
" case let x: i32 => { r = 1; };\n"
|
||||
" };\n"
|
||||
" return r: int;\n"
|
||||
"};\n",
|
||||
7 },
|
||||
|
||||
/* (6) #3/F1-fold — a deduped `(i32|i32|void)` returned via bare
|
||||
* `return;` must tag the void variant off the NORMALIZED chain (idx 1),
|
||||
* matching the dedup'd construct/match numbering. A raw-list
|
||||
* voidvariantindex tags it 2 -> no match arm fires -> r stays 0. The
|
||||
* void arm yields 5. */
|
||||
{ "void_dedup_ret",
|
||||
"package main;\n"
|
||||
"type u = (i32 | i32 | void);\n"
|
||||
"fn f() u = { return; };\n"
|
||||
"export fn main() int = {\n"
|
||||
" let v: u = f();\n"
|
||||
" let r: i64 = 0;\n"
|
||||
" match (v) {\n"
|
||||
" case void => { r = 5; };\n"
|
||||
" case let x: i32 => { r = 9; };\n"
|
||||
" };\n"
|
||||
" return r: int;\n"
|
||||
"};\n",
|
||||
5 },
|
||||
|
||||
/* (7) #3/F1-fold — void at NORMALIZED index 0 pins the lower
|
||||
* boundary of voidvariantindex's idx counter (the only position the
|
||||
* missing `idx += 1` happened to get right). `(void|i32)` keeps void
|
||||
* first; bare `return;` tags idx 0, the void arm yields 5. */
|
||||
{ "void_pos0_ret",
|
||||
"package main;\n"
|
||||
"type u = (void | i32);\n"
|
||||
"fn f() u = { return; };\n"
|
||||
"export fn main() int = {\n"
|
||||
" let v: u = f();\n"
|
||||
" let r: i64 = 0;\n"
|
||||
" match (v) {\n"
|
||||
" case void => { r = 5; };\n"
|
||||
" case let x: i32 => { r = 9; };\n"
|
||||
" };\n"
|
||||
" return r: int;\n"
|
||||
"};\n",
|
||||
5 },
|
||||
|
||||
/* (8) #3/F1-fold — void at NORMALIZED index 2 is the stronger teeth
|
||||
* for the idx counter: a raw-list / non-incrementing voidvariantindex
|
||||
* tags it 0 (the i32 arm) instead of 2, so r never reaches 5.
|
||||
* `(i32|str|void)` has no dedup/drop, so void's normalized index IS
|
||||
* its declaration index 2. */
|
||||
{ "void_pos2_ret",
|
||||
"package main;\n"
|
||||
"type u = (i32 | str | void);\n"
|
||||
"fn f() u = { return; };\n"
|
||||
"export fn main() int = {\n"
|
||||
" let v: u = f();\n"
|
||||
" let r: i64 = 0;\n"
|
||||
" match (v) {\n"
|
||||
" case void => { r = 5; };\n"
|
||||
" case let s: str => { r = 8; };\n"
|
||||
" case let x: i32 => { r = 9; };\n"
|
||||
" };\n"
|
||||
" return r: int;\n"
|
||||
"};\n",
|
||||
5 },
|
||||
};
|
||||
|
||||
/* run_build — build+run `src` via `driver`; returns the binary's exit
|
||||
* code, or -1 on a build failure. */
|
||||
static int
|
||||
run_build(const char *driver, const struct row *r, int i)
|
||||
{
|
||||
char src[128], tmpdir[64], outbin[128], rmcmd[160], cmd[1024];
|
||||
snprintf(tmpdir, sizeof tmpdir, "/tmp/tagnorm_%d_d_%d", getpid(), i);
|
||||
mkdir(tmpdir, 0755);
|
||||
snprintf(src, sizeof src, "%s/tagnorm_%d_%d.ww", tmpdir, getpid(), i);
|
||||
snprintf(outbin, sizeof outbin, "%s/tagnorm_%d_%d", tmpdir, getpid(), i);
|
||||
snprintf(rmcmd, sizeof rmcmd, "rm -rf %s", tmpdir);
|
||||
|
||||
FILE *f = fopen(src, "wb");
|
||||
if (!f) { runwait(rmcmd); return -2; }
|
||||
fputs(r->src, f);
|
||||
fclose(f);
|
||||
|
||||
snprintf(cmd, sizeof cmd, "%s build -o %s %s 2>/dev/null",
|
||||
driver, outbin, src);
|
||||
int brc = runwait(cmd);
|
||||
|
||||
int got = -1;
|
||||
if (brc == 0) got = runwait(outbin);
|
||||
|
||||
runwait(rmcmd);
|
||||
return brc == 0 ? got : -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
const char *bin = getenv("BIN");
|
||||
if (!bin) bin = "out/bin";
|
||||
char absbin[1024];
|
||||
if (bin[0] != '/') {
|
||||
char cwd[1024];
|
||||
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
|
||||
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
|
||||
bin = absbin;
|
||||
}
|
||||
|
||||
char cdrv[1024], wdrv[1024];
|
||||
snprintf(cdrv, sizeof cdrv, "%s/ww", bin);
|
||||
snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin);
|
||||
|
||||
struct { const char *name; const char *drv; int gated; }
|
||||
drivers[] = {
|
||||
{ "cstage", cdrv, 0 },
|
||||
{ "wwstage", wdrv, 1 },
|
||||
{ NULL, NULL, 0 },
|
||||
};
|
||||
|
||||
int n = (int)(sizeof rows / sizeof rows[0]);
|
||||
int total = 0, fail = 0;
|
||||
|
||||
for (int d = 0; drivers[d].name; d++) {
|
||||
if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) {
|
||||
fprintf(stderr, "tagnorm_run: skip %s (no %s)\n",
|
||||
drivers[d].name, drivers[d].drv);
|
||||
continue;
|
||||
}
|
||||
for (int i = 0; i < n; i++) {
|
||||
total++;
|
||||
int got = run_build(drivers[d].drv, &rows[i], i);
|
||||
if (got != rows[i].want_exit) {
|
||||
fprintf(stderr, "tagnorm_run[%s][%s]: exit=%d "
|
||||
"want=%d\n", drivers[d].name, rows[i].label,
|
||||
got, rows[i].want_exit);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fail) {
|
||||
fprintf(stderr, "tagnorm_run: %d/%d fixtures failed\n",
|
||||
fail, total);
|
||||
return 1;
|
||||
}
|
||||
printf("tagnorm_run: %d/%d ok\n", total, total);
|
||||
return 0;
|
||||
}
|
||||
14
test/wcc/data/idxtagfield_big_assign/case.ww
Normal file
14
test/wcc/data/idxtagfield_big_assign/case.ww
Normal file
@@ -0,0 +1,14 @@
|
||||
//ww:error "#58: >32B tagged-field indexed store unreachable until #114"
|
||||
// 944_idx_tagged_field big_assign tripwire: a >32B box STORE via xs[i].m
|
||||
// LOUD-STOPS in BOTH stages until #114. A SUCCESS = loud-stop regressed to silent.
|
||||
package main;
|
||||
type big = (void | size | [5]size);
|
||||
type row = struct { id: int, m: big };
|
||||
export fn main() i32 = {
|
||||
let xs: [2]row = [
|
||||
row { id = 1, m = 7: size },
|
||||
row { id = 2, m = 8: size },
|
||||
];
|
||||
xs[0].m = 9: size;
|
||||
return 0;
|
||||
};
|
||||
16
test/wcc/data/idxtagfield_big_read/case.ww
Normal file
16
test/wcc/data/idxtagfield_big_read/case.ww
Normal file
@@ -0,0 +1,16 @@
|
||||
//ww:error "#58: >32B tagged-field indexed read unreachable until #114"
|
||||
// 944_idx_tagged_field big_read tripwire: a >32B box read via the indexed spine
|
||||
// (xs[i].m) LOUD-STOPS in BOTH stages (rule 7) until #114 wires >32B box memcpy.
|
||||
// Reachable via a NARROW init; a SUCCESS here = the loud-stop regressed to silent.
|
||||
package main;
|
||||
type big = (void | size | [5]size);
|
||||
type row = struct { id: int, m: big };
|
||||
export fn main() i32 = {
|
||||
let xs: [2]row = [
|
||||
row { id = 1, m = 7: size },
|
||||
row { id = 2, m = 8: size },
|
||||
];
|
||||
let v: size = xs[0].m as size;
|
||||
if (v != 7) { return 1; };
|
||||
return 0;
|
||||
};
|
||||
14
test/wcc/data/idxtagfield_float_assign/case.ww
Normal file
14
test/wcc/data/idxtagfield_float_assign/case.ww
Normal file
@@ -0,0 +1,14 @@
|
||||
//ww:error "#58: float-payload tagged-field indexed store unreachable until #114"
|
||||
// 944_idx_tagged_field float_assign tripwire: a float-payload box STORE via
|
||||
// xs[i].m LOUD-STOPS in BOTH stages until #114. SUCCESS = loud-stop regressed.
|
||||
package main;
|
||||
type fo = (void | f64);
|
||||
type row = struct { id: int, m: fo };
|
||||
export fn main() i32 = {
|
||||
let xs: [2]row = [
|
||||
row { id = 1, m = 1.0: f64 },
|
||||
row { id = 2, m = 2.0: f64 },
|
||||
];
|
||||
xs[0].m = 3.0: f64;
|
||||
return 0;
|
||||
};
|
||||
14
test/wcc/data/idxtagfield_midword_assign/case.ww
Normal file
14
test/wcc/data/idxtagfield_midword_assign/case.ww
Normal file
@@ -0,0 +1,14 @@
|
||||
//ww:error "#58: multi-word tagged-field indexed store unreachable until #114"
|
||||
// 944_idx_tagged_field midword_assign tripwire: a multi-word (>16B, <=32B box)
|
||||
// STORE via xs[i].m LOUD-STOPS in BOTH stages until #114. SUCCESS = regressed.
|
||||
package main;
|
||||
type mid = (void | size | [3]size);
|
||||
type row = struct { id: int, m: mid };
|
||||
export fn main() i32 = {
|
||||
let xs: [2]row = [
|
||||
row { id = 1, m = 7: size },
|
||||
row { id = 2, m = 8: size },
|
||||
];
|
||||
xs[0].m = 9: size;
|
||||
return 0;
|
||||
};
|
||||
16
test/wcc/data/nestunion_collision/case.ww
Normal file
16
test/wcc/data/nestunion_collision/case.ww
Normal file
@@ -0,0 +1,16 @@
|
||||
//ww:error "structural fallback cannot disambiguate nominally-distinct same-shape variants"
|
||||
// 925_nested_union_widen collision_guard: two distinct names a,b both !(i32|bool)
|
||||
// in one union (a | b) — without nominal layout the widen cannot pick a variant.
|
||||
// BOTH stages must loud-stop (runww ERROR arm runs w6c AND w6c_ww). Migrated from
|
||||
// 989-era 925_nested_union_widen_run.c (#5-C5).
|
||||
package main;
|
||||
type a = !(i32 | bool);
|
||||
type b = !(i32 | bool);
|
||||
export fn main() i32 = {
|
||||
let x: a = (1i32: a);
|
||||
let r: (a | b) = x;
|
||||
match (r) {
|
||||
case let p: a => return 1;
|
||||
case let q: b => return 2;
|
||||
};
|
||||
};
|
||||
15
test/wcc/data/vchain_amb_loud/case.ww
Normal file
15
test/wcc/data/vchain_amb_loud/case.ww
Normal file
@@ -0,0 +1,15 @@
|
||||
//ww:error "source alias chain reaches >=2 variants"
|
||||
// 944_variant_chain_b95 chain_amb_loud: source base reaches both ali and ali2
|
||||
// (chain) — ambiguous without nominal layout (#95). BOTH stages loud-stop.
|
||||
package main;
|
||||
type base = struct { a: int, b: int };
|
||||
type ali = base;
|
||||
type ali2 = ali;
|
||||
export fn main() i32 = {
|
||||
let s: base;
|
||||
s.a = 4; s.b = 9;
|
||||
let v: (ali | ali2) = s;
|
||||
if (v is ali) { return 1; };
|
||||
if (v is ali2) { return 2; };
|
||||
return 3;
|
||||
};
|
||||
11
test/wcc/data/vchain_bare_ambig/case.ww
Normal file
11
test/wcc/data/vchain_bare_ambig/case.ww
Normal file
@@ -0,0 +1,11 @@
|
||||
//ww:error "bare source structurally matches >=2 NAMED variants"
|
||||
// 944_variant_chain_b95 bare_ambig: a bare *i32 source matches both p1 and p2
|
||||
// (#15/#218). BOTH stages loud-stop.
|
||||
package main;
|
||||
type p1 = *i32;
|
||||
type p2 = *i32;
|
||||
export fn main() i32 = {
|
||||
let x: i32 = 7;
|
||||
let v: (p1 | p2) = &x;
|
||||
return 0;
|
||||
};
|
||||
13
test/wcc/data/vchain_bare_ambig2/case.ww
Normal file
13
test/wcc/data/vchain_bare_ambig2/case.ww
Normal file
@@ -0,0 +1,13 @@
|
||||
//ww:error "bare source structurally matches >=2 NAMED variants"
|
||||
// 944_variant_chain_b95 bare_ambig2: bare *i32 reaches q1,q2 via b1/b2 chains
|
||||
// (#15/#218). BOTH stages loud-stop.
|
||||
package main;
|
||||
type b1 = *i32;
|
||||
type b2 = *i32;
|
||||
type q1 = b1;
|
||||
type q2 = b2;
|
||||
export fn main() i32 = {
|
||||
let x: i32 = 7;
|
||||
let v: (q1 | q2) = &x;
|
||||
return 0;
|
||||
};
|
||||
13
test/wcc/data/vchain_isas_amb/case.ww
Normal file
13
test/wcc/data/vchain_isas_amb/case.ww
Normal file
@@ -0,0 +1,13 @@
|
||||
//ww:error "not a variant"
|
||||
// 944_variant_chain_b95 isas_amb_reject_notcrash: `is base` on (ali | ali2)
|
||||
// (both reach base) — must REJECT (not crash) on the ambiguity (#107). BOTH
|
||||
// stages reject with the diagnostic body, not a signal.
|
||||
package main;
|
||||
type base = struct { a: int, b: int };
|
||||
type ali = base;
|
||||
type ali2 = ali;
|
||||
fn f(v: (ali | ali2)) i32 = {
|
||||
if (v is base) { return 1; };
|
||||
return 0;
|
||||
};
|
||||
export fn main() i32 = { return 0; };
|
||||
12
test/wcc/data/vchain_isas_chain/case.ww
Normal file
12
test/wcc/data/vchain_isas_chain/case.ww
Normal file
@@ -0,0 +1,12 @@
|
||||
//ww:error "not a variant"
|
||||
// 944_variant_chain_b95 isas_chain_reject: `is base` on (void | ali) where
|
||||
// ali=base — is/as ACCEPTANCE stays nominal-exact (#107). BOTH stages reject
|
||||
// (cstage "base is not a variant of (...)", wwstage "is/as: not a variant of...").
|
||||
package main;
|
||||
type base = struct { a: int, b: int };
|
||||
type ali = base;
|
||||
fn f(v: (void | ali)) i32 = {
|
||||
if (v is base) { return 1; };
|
||||
return 0;
|
||||
};
|
||||
export fn main() i32 = { return 0; };
|
||||
12
test/wcc/data/vchain_isas_unrel/case.ww
Normal file
12
test/wcc/data/vchain_isas_unrel/case.ww
Normal file
@@ -0,0 +1,12 @@
|
||||
//ww:error "not a variant"
|
||||
// 944_variant_chain_b95 isas_unrel_reject: `is ta` on (void | tb) where ta,tb
|
||||
// are same-shape but distinct names — is/as acceptance is nominal-exact (#107).
|
||||
// BOTH stages reject.
|
||||
package main;
|
||||
type ta = struct { a: int, b: int };
|
||||
type tb = struct { a: int, b: int };
|
||||
fn f(v: (void | tb)) i32 = {
|
||||
if (v is ta) { return 1; };
|
||||
return 0;
|
||||
};
|
||||
export fn main() i32 = { return 0; };
|
||||
12
test/wcc/data/vchain_twin_prim_amb/case.ww
Normal file
12
test/wcc/data/vchain_twin_prim_amb/case.ww
Normal file
@@ -0,0 +1,12 @@
|
||||
//ww:error "source alias chain reaches >=2 variants"
|
||||
// 944_variant_chain_b95 twin_prim_alias_amb: union (int | ai) ai=int, source aj=int
|
||||
// — both the bare int variant and ai share the int bottom, ambiguous (#95
|
||||
// deviation-2). BOTH stages loud-stop.
|
||||
package main;
|
||||
type ai = int;
|
||||
type aj = int;
|
||||
export fn main() i32 = {
|
||||
let s: aj = 7;
|
||||
let v: (int | ai) = s;
|
||||
return 0;
|
||||
};
|
||||
Reference in New Issue
Block a user