test: migrate str-elem-cap/size-in-type behavior to test/lang @test
Two more value-observable families ported additively (the .c sources
keep running in $(TESTS); de-dup deferred to fold 6).
932_str_elem_cap -> str_elem_cap_test: a str-element N_INDEX value
read must load the full 24B {ptr,len,cap}, not {ptr,len} (F2); each
shape poisons cap != len so a 2-word read fails the .cap assert.
Template for the 933-939 cap family.
957_size_type -> size_type_test: `size` binds in type position and
coexists with the size(T) operator (#85); a green row is the bind
proof.
This commit is contained in:
54
test/lang/size_type_test.ww
Normal file
54
test/lang/size_type_test.ww
Normal file
@@ -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);
|
||||
};
|
||||
58
test/lang/str_elem_cap_test.ww
Normal file
58
test/lang/str_elem_cap_test.ww
Normal file
@@ -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 = <index>` (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);
|
||||
};
|
||||
Reference in New Issue
Block a user