package reject_test; // Cstage-only loud-reject pins, ported from the retired native // carriers test/wcc/760_def_widen_const.c and // test/wcc/783_amp_fn_assign_run.c. Each row asserts ONLY the cstage // reject; the wwstage side over-accepts today and that over-accept is // documented here (residual audit, test-infra-v2-residual-20260808), // never asserted as desired — these rows graduate to //ww:error // corpus fixtures the moment the wwstage checker gains the matching // reject. // // 760 (#113 def_cast_fits): an out-of-range narrowing def-ref must // fail the cstage build loud — the const-scoped widen must never // become a silent truncation. wwstage has no def-init assignability // check at all (pre-existing leanness) and accepts. The r76_def_widen_* // byte-id legs the carrier also drove are owned by the // test-data-byteid blanket over those fixtures. // // 783 (#206 gate, tagged rows): the two NEGATIVE tagged-slot rows are // cstage-only pending #214 — wwstage's `(X | void)` tagged // assignability leniently matches any *fn against the void variant // (the accept-INVALID hole; its close is required before wwstage can // be the authoritative checker). The positive byte-id rows are owned // by the r78_amp_fn_assign_* fixtures plus the test-data-byteid // blanket; the symmetric non-tagged rejects by the corpus // r783_amp_fn_assign_neg_* //ww:error fixtures. import os; import os.exec; import strings; import testenv; import time; fn fail(label: str, why: str) void = { let m: str = strings.concat("reject FAIL: ", label, " -- ", why, "\n"); os.write(2, m.ptr, m.len: u64); assert(false); }; fn tmo() time.duration = { return (180i64 * (time.second: i64)): time.duration; }; @test fn defwidennarrow() void = { let td: str = testenv.fresh(); testenv.writefile(strings.concat(td, "/nw.ww"), strings.concat( "package main;\n", "def BIG_I32: i32 = 70000;\n", "def S_I16: i16 = BIG_I32;\n", "export fn main() i32 = { let s: i16 = S_I16; ", "return s: i32; };\n")); let av: []str = [testenv.driver("w6c"), "-o", strings.concat(td, "/nw.s"), strings.concat(td, "/nw.ww")]; let co: testenv.commandout; testenv.runcommand(td, td, "narrow", av, tmo(), &co); // A deadline kill is not a reject; only a loud EXIT failure is. if (co.termination != exec.termination.EXIT) { fail("narrow", "w6c did not terminate normally"); }; if (co.code == 0) { fail("narrow", strings.concat("w6c exited 0 (expected loud ", "failure, no silent truncation)")); }; testenv.clean(td); }; fn cstagerejectrow(label: str, src: str) void = { let td: str = testenv.fresh(); testenv.writefile(strings.concat(td, "/main783.ww"), src); let av: []str = [testenv.driver("ww"), "build", "-o", "main783", "main783.ww"]; let co: testenv.commandout; testenv.runcommand(td, td, label, av, tmo(), &co); if (co.termination != exec.termination.EXIT) { fail(label, "cstage build did not terminate normally"); }; if (co.code == 0) { fail(label, "cstage built but expected reject"); }; testenv.clean(td); }; @test fn ampfnassigntagged() void = { // Two same-signature ptr-to-fn variants: a direct &fn is ambiguous // and must never silently bind to one. cstagerejectrow("neg_ambiguous_tagged", strings.concat( "package main;\n", "type reader = fn(x: i32) i32;\n", "type writer = fn(x: i32) i32;\n", "fn myfn(x: i32) i32 = { return x; };\n", "fn main() i32 = {\n", " let x: (*reader | *writer | void) = &myfn;\n", " return 0;\n", "};\n")); // Laundering a materialized *fn into a (*reader|void) slot: // cstage rejects nominally (harec types.c:1039-1066 mirror). cstagerejectrow("neg_launder_tagged", strings.concat( "package main;\n", "type reader = fn(x: i32) i32;\n", "fn rd(x: i32) i32 = { return x + 1; };\n", "fn main() i32 = {\n", " let p = &rd;\n", " let s: (*reader | void) = p;\n", " return 0;\n", "};\n")); };