// 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); };