// lenidx_test — the `len()` builtin over a non-IDENT operand shape (#19, then // the C5 sweep #10 F2 + #41 FA2/FB1), migrated from test/wcc/802_lenidx_run.c. // `len(xs[i])` / `len(*p)` / `len(s.field)` etc. once fell to a bare cgexpr // fallback that left AX = the slice/str DATA POINTER, so len() returned the // PTR as the length — silent ptr-garbage, byte-identical in BOTH stages (the // bootstrap corpus never indexes-then-len()s an element, so byte-id was // blind). The fix routes every place-resolvable operand through one uniform // header-place arm (.len at place+8) in both stages; non-place operands // (string literal, slicing expr, call result) now die LOUD (rule 7) — those // three are the runww reject carriers. The empty/zero rows are the strongest // pins: a leaked ptr is nonzero, so `len(empty) == 0` catches the regression // where a bare exit code couldn't. PRIMITIVE-only asserts (no fmt/strconv) so // a co-miscompile in the assert path cannot mask the bug. The .len/.ptr // pseudo-field control pins that the fix left the already-correct field paths // untouched. T2 keeps cs==ww (the #19 lineage: len of an INDEXED element is // exactly where cs!=ww would surface). package lenidx_test; // global [N]str table — the exact #19 repro operand (static-init str array, // #18 family); referenced from several rows to keep the global codegen path. let t: [3]str = ["a", "bcd", ""]; type S = struct { pad: i64, name: str }; @test fn lenidx_arrtab_mid() void = { // len(t[1]) == 3; pre-fix this returned a ptr low-byte, not 3. assert(len(t[1]) == 3); }; @test fn lenidx_arrtab_empty() void = { // empty element: a leaked ptr is nonzero, len must be 0. assert(len(t[2]) == 0); }; @test fn lenidx_arrtab_first() void = { // guards an off-by-one in the index scale. assert(len(t[0]) == 1); }; fn slen(xs: []str, i: i64) int = { return len(xs[i]): int; }; @test fn lenidx_slice_param_sum() void = { // len(xs[i]) over a []str slice ARG (callee-side shape), 1 + 3 + 0 == 4. let arr: [3]str = ["a", "bcd", ""]; let xs: []str = arr; assert(slen(xs, 0) + slen(xs, 1) + slen(xs, 2) == 4); }; @test fn lenidx_neg_control_field_agree() void = { // the `.len` pseudo-field on the same indexed element must agree with // len(), AND `.ptr` must still deref to the first byte — the fix must // leave the already-correct .len/.ptr field paths untouched. assert(len(t[1]) == t[1].len); let p: *u8 = t[1].ptr; assert(*p == 'b'); assert(len(t[1]) == 3); }; fn derefn(p: *[]i64) int = { return len(*p): int; }; @test fn lenidx_deref_param() void = { // len(*p) through a *[]T PARAM (FB1) loaded the header's word 0 (the // data pointer) as the length. let xs: []i64 = [10, 20, 30]; assert(derefn(&xs) == 3); }; @test fn lenidx_deref_local() void = { let xs: []i64 = [10, 20, 30]; let p: *[]i64 = &xs; assert(len(*p) == 3); }; @test fn lenidx_deref_empty() void = { // EMPTY-slice deref: the off-by-header bug returns .ptr (nonzero), // len() must say 0 — only a non-exit primitive assert pins this. let xs: []i64 = [1]; let ys: []i64 = xs[0:0]; let p: *[]i64 = &ys; assert(len(*p) == 0); }; @test fn lenidx_deref_chain() void = { // chained deref len(**pp) — the resolver spine must recurse both hops. let xs: []i64 = [10, 20, 30]; let p: *[]i64 = &xs; let pp: **[]i64 = &p; assert(len(**pp) == 3); }; @test fn lenidx_idx_field() void = { // len(xs[i].field) (F2) — N_DOT over an N_INDEX base matched no arm. let xs: [2]S = [S{ pad = 1, name = "a" }, S{ pad = 2, name = "bcde" }]; assert(len(xs[1].name) == 4); }; fn derefidxn(p: *[]S, i: i64) int = { return len((*p)[i].name): int; }; @test fn lenidx_deref_idx_field() void = { // full deref spine: len((*p)[i].field). let arr: [2]S = [S{ pad = 1, name = "a" }, S{ pad = 2, name = "bcde" }]; let xs: []S = arr; assert(derefidxn(&xs, 1) == 4); }; fn derefidxcn(p: *[]S, i: i64) int = { return len((*p)[i + 1].name): int; }; @test fn lenidx_deref_idx_field_computed() void = { // computed index through the spine — i+1 must resolve like a constant. let arr: [2]S = [S{ pad = 1, name = "a" }, S{ pad = 2, name = "bcde" }]; let xs: []S = arr; assert(derefidxcn(&xs, 0) == 4); }; @test fn lenidx_dot_field() void = { // len(s.field) — the #235 arm's inner non-tuple fallback was ptr-garbage. let s: S = S{ pad = 7, name = "abc" }; assert(len(s.name) == 3); }; @test fn lenidx_ptr_field() void = { // len(p.field) through a *struct. let s: S = S{ pad = 7, name = "abc" }; let p: *S = &s; assert(len(p.name) == 3); }; @test fn lenidx_neutral_array_const() void = { // neutrality control — the [N]T const fast-path still holds. let a: [5]u8 = [1, 2, 3, 4, 5]; assert(len(a) == 5); }; @test fn lenidx_neutral_ident_str() void = { // neutrality control — the ident-str fast-path still holds. let s: str = "abcd"; assert(len(s) == 4); };