Files
ww/test/lang/unwrap_callarg_str_test.ww
Hojun-Cho 83f5956df2 test: banner purge + WHY-only comment sweep (rule 8)
Every section banner dies (103 -> 0) across test/lang, the observer
suites, the C carriers, and the five comment-heavy corpus fixtures;
banner provenance (#N cites, carrier numbers, repair-cluster labels)
folded into headers or adjacent WHY comments. Narration deleted; row
provenance, ref cites, divergence pins, and layout contracts kept
(fwd-ref decl-order guards and bootstrap-gate corpus rationale
restored where the sweep over-cut). Comment-only proven: all 3742
wwbuild workdir .s byte-identical before/after; test-commit and
test-byteid (161 lang + 1399 data, 0 pinned-divergent) green.
2026-08-08 21:40:23 +09:00

44 lines
1.9 KiB
Plaintext

// Runtime contract for a whole 24B str header
// produced by an UNWRAP source (`mk_se()!` N_TRYUNW) passed BY VALUE as a
// call argument. #6 (Mechanism A): cgenutil.ww's nodeisstr had no
// N_TRYUNW/N_TRYPROP arm, so the unwrap fell through to the scalar
// single-PUSHQ default — pushargsrev marshalled only .ptr (1 word) and
// cgcall's pop sizer drained one word, so the callee read .len/.cap from
// stale arg registers. cstage (cmd/w6c/cgen.c node_isstr) was always
// type-keyed and correct; this aligns wwstage UP. Twin of #9's
// deref_callarg str pin — same masking mechanism, different node kind.
//
// WHY the poison call: a pre-fix `strlen(mk_se()!)` pushes only .ptr, so
// the callee reads .len from whatever arg register the caller left. The
// preceding NORMAL-arg call strlen(decoy) deterministically leaves the
// callee's .len register holding decoy's len 2 (≠ backing's 5), so a
// pre-fix 1-word push reads 2 and the assert REDDENS. This is a wwstage-
// only gap (cs≠ww), so the tooth is byte-id (cstage already pushed 3) AND
// value-under-the-wwstage-frontend; byte-id (990-996) proves the stages
// AGREE, not that the code is correct.
package unwrap_callarg_str_test;
type e = !i32;
fn mk_se() (str | e) = { return "abcde"; };
fn strlen(s: str) i32 = { return s.len: i32; };
fn strcap(s: str) i32 = { return s.cap: i32; };
fn strfirst(s: str) i32 = { return s[0]: i32; };
@test fn str_unwrap_callarg() void = {
// Decoy ("xy", len 2) poisons the callee's .len register via a
// normal-arg call; the unwrap-source call must overwrite it with 5.
let decoy: str = "xy";
assert(strlen(decoy) == 2);
assert(strlen(mk_se()!) == 5);
// .cap is the 3rd dropped word; decoy ("xy") seeds cap 2, so a
// pre-fix 1-word push leaves the callee reading 2 here, not 5.
assert(strcap(mk_se()!) == 5);
// strfirst pins .ptr survived the push ('a' == 97).
assert(strfirst(mk_se()!) == 97);
};