diff --git a/test/lang/size_type_test.ww b/test/lang/size_type_test.ww new file mode 100644 index 00000000..7853b846 --- /dev/null +++ b/test/lang/size_type_test.ww @@ -0,0 +1,54 @@ +// size_type_test — the `size` type name must be usable in TYPE position (#85 +// fold-2) AND coexist with the `size(T)` size-of OPERATOR, migrated from +// test/wcc/957_size_type_run.c. The two are different paths: TYPE-position +// `size` resolves via the type-name resolver (lookup_builtin / N_TNAME chain), +// while `size(T)` is a c.top SK_FN seed folded to an N_INTLIT — so a program +// may use BOTH in the same scope with no clash. Without the fold-2 resolver +// arm a `let _: size` declaration fails to compile ("unknown type 'size'"), so +// a GREEN row IS the bind proof: if `size` didn't bind as a type, the file +// would not compile. `size` mirrors uintptr: platform-width (8B amd64) +// unsigned int. cstage-only by design (mirrors 951/952/969); the wwstage +// resolver arm is twinned for rule-10 symmetry and stays dead on the size-free +// selfhost corpus. +// +// The rows vary the size-typed value (0, 42, sum, struct field) and the +// consuming context (comparison, arithmetic, i32-cast); the SHAPE — `size` in +// a let-binding vs a struct-field type — is what the bug gates on, so the +// shapes are spread across @test fns and the operator-coexistence row sits +// alongside the type-position rows. + +package size_type_test; + +type box = struct { n: size }; + +@test fn size_type_and_operator_coexist() void = { + // type-position `size` + the size(T) operator in one scope; both resolve + // and agree (8/4 on amd64). + let x: size = 0; + assert(x == 0); + let y: size = 42; + assert(y == 42); + assert(size(int) == 8); + assert(size(i32) == 4); +}; + +@test fn size_arith_cast() void = { + // a size-typed sum truncates to i32 and propagates (40 + 2 == 42). + let x: size = 40; + let y: size = 2; + let r: i32 = (x + y): i32; + assert(r == 42); +}; + +@test fn size_struct_field() void = { + // `size` as a struct field type: 8-byte slot, written and read back. + let b: box = box{ n = 7 }; + assert(b.n: i32 == 7); +}; + +@test fn size_operator_cast() void = { + // the size(T) operator value-propagated through an i32 cast (operator + // path, must keep working alongside the type-name arm). + let r: i32 = size(int): i32; + assert(r == 8); +}; diff --git a/test/lang/str_elem_cap_test.ww b/test/lang/str_elem_cap_test.ww new file mode 100644 index 00000000..a00766e6 --- /dev/null +++ b/test/lang/str_elem_cap_test.ww @@ -0,0 +1,58 @@ +// str_elem_cap_test — a str-element VALUE read via N_INDEX must load the full +// 24B {ptr,len,cap} header, not just {ptr,len}, migrated from test/wcc/ +// 932_str_elem_cap_run.c (F2 fold). str is 24B since Phase 2 (#1); pre-F2 the +// N_INDEX str-element arms dropped the cap word, so reading `.cap` off an +// indexed str element returned garbage (the missing third word) — cs==ww held, +// so the 990-997 byte-id gates were GREEN while the runtime was wrong. +// +// Each shape POISONS the element so cap != len (a `.cap =` pseudo-field write, +// no malloc / no import), then observes cap through `let e: str = ` (a +// 3-word copy into the slot) and `e.cap` (an N_IDENT pseudo-field read off the +// slot). A 2-word read leaves cap = len (N_IDENT base) or a stale slice-cap +// (fallback base), so the read-back .cap mismatches the poisoned value. .len is +// the control. The shapes (index base form), not the data, are what vary, so +// per-shape asserts — this file is the template for the 933-939 cap family. + +package str_elem_cap_test; + +type box = struct { items: []str }; + +@test fn sitea_ident_base() void = { + // N_IDENT base: xs[0] on a local [N]str, constant index. Poison cap=8 + // (len=2); a broken 2-word read leaves cap = len = 2. + let p: str = "hi"; + p.cap = 8i32; + let xs: [2]str; + xs[0] = p; + let e: str = xs[0]; + assert(e.cap: i32 == 8); + assert(e.len: i32 == 2); +}; + +@test fn sitea_var_index() void = { + // N_IDENT base indexed by a VARIABLE (not a constant) — the index + // source must not change which words the element load reads. + let p: str = "hey"; + p.cap = 12i32; + let xs: [2]str; + let i: i32 = 1; + xs[i] = p; + let e: str = xs[i]; + assert(e.cap: i32 == 12); + assert(e.len: i32 == 3); +}; + +@test fn siteb_fallback_base() void = { + // Fallback base: the element sits behind an N_DOT slice field, so the + // N_INDEX base isn't a plain ident. Poison cap=9 (len=5); a broken read + // leaves cap = the slice header's own cap, not the element's. + let q: str = "world"; + q.cap = 9i32; + let arr: [2]str; + arr[0] = q; + let b: box; + b.items = arr[0:2]; + let e: str = b.items[0]; + assert(e.cap: i32 == 9); + assert(e.len: i32 == 5); +};