test: migrate 933-939 str-cap family to test/lang @test
Fan out the str-cap read (933-936) and store (937-939) families into in-language @test files, following the 932 str_elem_cap template. Additive: the *_run.c stay in the C corpus (they are the only wwstage-runtime net for these cs==ww byte-id-blind shapes); de-dup deferred to fold 6. Per-shape @test fns, not a data table: each fn varies the codegen shape (base reg / chain depth / tuple return-ABI / store position), so the row-array idiom (blocked by #111) would lose coverage. Read family keeps the spoil()/register-clobber + junk==44 discrimination where the .c has it; store family pre-poisons the slot via a path distinct from the store under test. Asserts are primitives only.
This commit is contained in:
81
test/lang/str_arrfield_cap_test.ww
Normal file
81
test/lang/str_arrfield_cap_test.ww
Normal file
@@ -0,0 +1,81 @@
|
||||
// str_arrfield_cap_test — a str-typed FIELD of an INDEXED element `arr[i].f`
|
||||
// (N_INDEX-rooted N_DOT) must load the full 24B {ptr,len,cap} header, not just
|
||||
// {ptr,len}, migrated from test/wcc/936_str_arrfield_cap_run.c (C4.6 arrfield,
|
||||
// the LAST member of the 3-word-value-read cluster). str is 24B since Phase 2
|
||||
// (#1); pre-arrfield the `arr[i].f` str arm loaded 2 words (len in BX, ptr in
|
||||
// AX) and dropped cap. The miscompile was cs==ww, so the 990-997 byte-id gates
|
||||
// stayed GREEN while the runtime was wrong — behavioral @test is the net. There
|
||||
// is no adjacent slice-element sibling at this leaf, so the 3-word triple is
|
||||
// authored directly to the canonical slice-header ABI (len->BX, cap->CX,
|
||||
// ptr->AX LAST).
|
||||
//
|
||||
// POISONING: the store side of `arr[i].f = v` is a separate, still-broken
|
||||
// store-side gap (filed; out of scope for this read-side fold). So the value
|
||||
// elements (A, B) are poisoned through `&arr[i]` + a *struct field write, which
|
||||
// lands a real cap into the element's +16 word; D points its element at a
|
||||
// separately-built struct (the proven s1local 3-word field store).
|
||||
//
|
||||
// DISCRIMINATION: a 2-word read leaves CX holding whatever the index scale-
|
||||
// multiply left there, never the poison; spoil() additionally interposes a
|
||||
// CX-clobbering call between the build and the `arr[i].f` read, so a broken
|
||||
// 2-word read observes spoil's leftover (44), never the poison cap.
|
||||
|
||||
package str_arrfield_cap_test;
|
||||
|
||||
type rec = struct { f: str };
|
||||
|
||||
fn spoil() i32 = {
|
||||
let z: str = "zzzz";
|
||||
z.cap = 44i32;
|
||||
let w: str = z;
|
||||
return w.cap: i32;
|
||||
};
|
||||
|
||||
@test fn arrfield_array_value() void = {
|
||||
// A — `arr[i].f` value element of a [N]rec local array (LEAQ base).
|
||||
// Poison cap=8 (len=2) via &arr[1] + a *struct field store.
|
||||
let p: str = "hi";
|
||||
p.cap = 8i32;
|
||||
let arr: [3]rec;
|
||||
let pr: *rec = &arr[1];
|
||||
pr.f = p;
|
||||
let junk: i32 = spoil();
|
||||
let s: str = arr[1].f;
|
||||
assert(s.cap: i32 == 8);
|
||||
assert(s.len: i32 == 2);
|
||||
assert(junk == 44);
|
||||
};
|
||||
|
||||
@test fn arrfield_slice_value() void = {
|
||||
// B — `sl[i].f` value element of a []rec local slice (MOVQ slice.ptr
|
||||
// base) viewing the same poisoned backing array. Poison cap=9 (len=5).
|
||||
let p: str = "world";
|
||||
p.cap = 9i32;
|
||||
let arr: [3]rec;
|
||||
let pr: *rec = &arr[1];
|
||||
pr.f = p;
|
||||
let sl: []rec = arr[0:3];
|
||||
let junk: i32 = spoil();
|
||||
let s: str = sl[1].f;
|
||||
assert(s.cap: i32 == 9);
|
||||
assert(s.len: i32 == 5);
|
||||
assert(junk == 44);
|
||||
};
|
||||
|
||||
@test fn arrfield_ptr_elem() void = {
|
||||
// D — `arr[i].f` pointer element of a [N]*rec array (LEAQ base, MOVQ
|
||||
// deref to the *rec, then the leaf field load). The element points at a
|
||||
// separately-built struct so the poison rides the proven s1local 3-word
|
||||
// field store, not the broken array-element store. Poison cap=7 (len=3).
|
||||
let p: str = "abc";
|
||||
p.cap = 7i32;
|
||||
let st: rec;
|
||||
st.f = p;
|
||||
let arr: [3]*rec;
|
||||
arr[1] = &st;
|
||||
let junk: i32 = spoil();
|
||||
let s: str = arr[1].f;
|
||||
assert(s.cap: i32 == 7);
|
||||
assert(s.len: i32 == 3);
|
||||
assert(junk == 44);
|
||||
};
|
||||
70
test/lang/str_arrfield_store_cap_test.ww
Normal file
70
test/lang/str_arrfield_store_cap_test.ww
Normal file
@@ -0,0 +1,70 @@
|
||||
// str_arrfield_store_cap_test — STORING a str into a FIELD of an INDEXED
|
||||
// element `arr[i].f = v` must write the full 24B {ptr,len,cap} header, not just
|
||||
// {ptr,len}, migrated from test/wcc/937_str_arrfield_store_cap_run.c (G1 fold).
|
||||
// str is 24B since Phase 2 (#1); the write-side mirror of the arrfield READ
|
||||
// (936) previously stored only 2 words (ptr@+0, len@+8) and silently DROPPED
|
||||
// cap. cs==ww held (byte-id-blind), so behavioral @test is the net.
|
||||
//
|
||||
// RHS cap!=len: each test str is a literal whose .cap is mutated to a value
|
||||
// DISTINCT from its len (a bare literal carries cap==len, hiding a dropped cap).
|
||||
// PRE-POISON: before the G1 store under test, all three slot words are seeded
|
||||
// with a DIFFERENT str (ptr='q', len=4, cap=5) via a PROVEN already-3-word store
|
||||
// path DISTINCT from G1 — A/B via &arr[i] + a *struct field store, D via the
|
||||
// proven s1local field store (st.f=q). A broken 2-word store never touches +16,
|
||||
// so the read-back observes the poison cap 5, never 8. The full-triple assert
|
||||
// (s[0]='h'=104, len=2, cap=8) catches a register slip that clobbers ptr/len.
|
||||
|
||||
package str_arrfield_store_cap_test;
|
||||
|
||||
type rec = struct { f: str };
|
||||
|
||||
@test fn arrfield_store_array_value() void = {
|
||||
// A — `arr[i].f = v` into a [N]S local array (LEAQ base, value
|
||||
// element). Poison the slot (cap=5,len=4,'q') via &arr[1] + a *struct
|
||||
// field store; then the G1 store lands the test str (cap=8,len=2,'h').
|
||||
let q: str = "qqqq"; q.cap = 5i32;
|
||||
let p: str = "hi"; p.cap = 8i32;
|
||||
let arr: [3]rec;
|
||||
let pr: *rec = &arr[1];
|
||||
pr.f = q;
|
||||
arr[1].f = p;
|
||||
let s: str = arr[1].f;
|
||||
assert(s.cap: i32 == 8);
|
||||
assert(s.len: i32 == 2);
|
||||
assert(s[0] == 104u8);
|
||||
};
|
||||
|
||||
@test fn arrfield_store_slice_value() void = {
|
||||
// B — `sl[i].f = v` into a []S local slice (MOVQ slice.ptr base, value
|
||||
// element). The slice views the same backing array; poison the element
|
||||
// through the array pointer, store and read back through the slice.
|
||||
let q: str = "qqqq"; q.cap = 5i32;
|
||||
let p: str = "hi"; p.cap = 8i32;
|
||||
let arr: [3]rec;
|
||||
let pr: *rec = &arr[1];
|
||||
pr.f = q;
|
||||
let sl: []rec = arr[0:3];
|
||||
sl[1].f = p;
|
||||
let s: str = sl[1].f;
|
||||
assert(s.cap: i32 == 8);
|
||||
assert(s.len: i32 == 2);
|
||||
assert(s[0] == 104u8);
|
||||
};
|
||||
|
||||
@test fn arrfield_store_ptr_elem() void = {
|
||||
// D — `arr[i].f = v` into a [N]*S pointer element (LEAQ base, MOVQ deref
|
||||
// to the *S, then store at the field). The element points at a
|
||||
// separately-built struct poisoned via the proven s1local field store
|
||||
// (st.f=q); the G1 store derefs arr[1] and overwrites st.f.
|
||||
let q: str = "qqqq"; q.cap = 5i32;
|
||||
let p: str = "hi"; p.cap = 8i32;
|
||||
let st: rec;
|
||||
st.f = q;
|
||||
let arr: [3]*rec;
|
||||
arr[1] = &st;
|
||||
arr[1].f = p;
|
||||
let s: str = arr[1].f;
|
||||
assert(s.cap: i32 == 8);
|
||||
assert(s.len: i32 == 2);
|
||||
assert(s[0] == 104u8);
|
||||
};
|
||||
43
test/lang/str_chained_field_cap_test.ww
Normal file
43
test/lang/str_chained_field_cap_test.ww
Normal file
@@ -0,0 +1,43 @@
|
||||
// str_chained_field_cap_test — a CHAINED str-field read `o.p.f` (depth >= 2,
|
||||
// where p is a *struct field of o and f is a str field of *p) must load the
|
||||
// full 24B {ptr,len,cap} header, not just {ptr,len}, migrated from
|
||||
// test/wcc/934_str_chained_field_cap_run.c (C4.6 case B). str is 24B since
|
||||
// Phase 2 (#1); pre-caseB the chained *struct N_DOT str arm loaded 2 words (ptr
|
||||
// in AX, len in BX) and dropped cap. The miscompile was cs==ww, so the 990-997
|
||||
// byte-id gates stayed GREEN while the runtime was wrong — behavioral @test is
|
||||
// the net. This is the chained sibling of str_field_cap_test (direct/single-
|
||||
// deref S1/S2): the lhs cgexpr leaves AX = the inner *struct pointer, then the
|
||||
// field is read off AX, cap from +16(AX), base (AX = ptr) read LAST.
|
||||
//
|
||||
// DISCRIMINATION: a 2-word read leaves CX untouched, so the row could
|
||||
// coincidentally pass if CX happened to still carry the poison cap. spoil()
|
||||
// interposes a CX-clobbering call between the field store and the chained read
|
||||
// (its own str copy leaves cap=44 in CX), so a broken read observes 44 not 8.
|
||||
|
||||
package str_chained_field_cap_test;
|
||||
|
||||
type inr = struct { f: str };
|
||||
type otr = struct { p: *inr };
|
||||
|
||||
fn spoil() i32 = {
|
||||
let z: str = "zzzz";
|
||||
z.cap = 44i32;
|
||||
let w: str = z;
|
||||
return w.cap: i32;
|
||||
};
|
||||
|
||||
@test fn caseb_chained_field() void = {
|
||||
// `o.p.f`: o a struct holding p: *inr, inr holding str field f. Poison
|
||||
// cap=8 (len=2).
|
||||
let p: str = "hi";
|
||||
p.cap = 8i32;
|
||||
let ist: inr;
|
||||
ist.f = p;
|
||||
let o: otr;
|
||||
o.p = &ist;
|
||||
let junk: i32 = spoil();
|
||||
let s: str = o.p.f;
|
||||
assert(s.cap: i32 == 8);
|
||||
assert(s.len: i32 == 2);
|
||||
assert(junk == 44);
|
||||
};
|
||||
59
test/lang/str_chainfield_store_cap_test.ww
Normal file
59
test/lang/str_chainfield_store_cap_test.ww
Normal file
@@ -0,0 +1,59 @@
|
||||
// str_chainfield_store_cap_test — STORING a str into a FIELD reached through a
|
||||
// *struct-VALUED EXPRESSION (`r.sym.f = v`, the chained-N_DOT store) must write
|
||||
// the full 24B {ptr,len,cap} header, not just {ptr,len}, migrated from
|
||||
// test/wcc/938_str_chainfield_store_cap_run.c (G2 fold). str is 24B since
|
||||
// Phase 2 (#1); the chained-store arm previously stored only 2 words (ptr@+0,
|
||||
// len@+8) and silently DROPPED cap. cs==ww held (byte-id-blind), so behavioral
|
||||
// @test is the net.
|
||||
//
|
||||
// RHS cap!=len: each test str is a literal whose .cap is mutated DISTINCT from
|
||||
// its len (a bare literal carries cap==len, hiding a dropped cap). PRE-POISON:
|
||||
// the three slot words are seeded with a DIFFERENT str (ptr='q', len=4, cap=5)
|
||||
// via the PROVEN already-3-word DIRECT field store (st.f=q, N_IDENT base) —
|
||||
// never the G2 chained store itself. A broken 2-word store never touches +16,
|
||||
// so the read-back observes the poison cap 5, never 8. The full-triple assert
|
||||
// (s[0]='h'=104, len=2, cap=8) catches a register slip that clobbers ptr/len.
|
||||
|
||||
package str_chainfield_store_cap_test;
|
||||
|
||||
type inner = struct { f: str };
|
||||
type outer2 = struct { sym: *inner };
|
||||
type mid = struct { b: *inner };
|
||||
type outer3 = struct { a: *mid };
|
||||
|
||||
@test fn chainfield_store_depth2() void = {
|
||||
// A — depth-2 `r.sym.f = v`. r.sym is a *inner pointing at a local st.
|
||||
// Poison st.f (cap=5,len=4,'q') via the DIRECT s.f=v 3-word store; then
|
||||
// the G2 chained store lands the test str (cap=8,len=2,'h') through r.sym.
|
||||
let q: str = "qqqq"; q.cap = 5i32;
|
||||
let p: str = "hi"; p.cap = 8i32;
|
||||
let st: inner;
|
||||
st.f = q;
|
||||
let r: outer2;
|
||||
r.sym = &st;
|
||||
r.sym.f = p;
|
||||
let s: str = r.sym.f;
|
||||
assert(s.cap: i32 == 8);
|
||||
assert(s.len: i32 == 2);
|
||||
assert(s[0] == 104u8);
|
||||
};
|
||||
|
||||
@test fn chainfield_store_depth3() void = {
|
||||
// B — depth-3 `r.a.b.f = v`. Two pointer hops (r.a: *mid, mid.b: *inner).
|
||||
// Poison the leaf via the DIRECT store (leaf.f=q); the G2 chained store
|
||||
// derefs r.a then .b and overwrites leaf.f. The deeper base eval clobbers
|
||||
// more registers, confirming the spilled triple survives.
|
||||
let q: str = "qqqq"; q.cap = 5i32;
|
||||
let p: str = "hi"; p.cap = 8i32;
|
||||
let leaf: inner;
|
||||
leaf.f = q;
|
||||
let m: mid;
|
||||
m.b = &leaf;
|
||||
let r: outer3;
|
||||
r.a = &m;
|
||||
r.a.b.f = p;
|
||||
let s: str = r.a.b.f;
|
||||
assert(s.cap: i32 == 8);
|
||||
assert(s.len: i32 == 2);
|
||||
assert(s[0] == 104u8);
|
||||
};
|
||||
68
test/lang/str_field_cap_test.ww
Normal file
68
test/lang/str_field_cap_test.ww
Normal file
@@ -0,0 +1,68 @@
|
||||
// str_field_cap_test — reading a str-typed FIELD of a struct (N_DOT value
|
||||
// read) must load the full 24B {ptr,len,cap} header, not just {ptr,len},
|
||||
// migrated from test/wcc/933_str_field_cap_run.c (C4.6 S1+S2 case A). str is
|
||||
// 24B since Phase 2 (#1); pre-C4.6 the N_DOT str-field arms loaded 2 words and
|
||||
// dropped cap. The miscompile was cs==ww, so the 990-997 byte-id gates stayed
|
||||
// GREEN while the runtime was wrong — a behavioral @test is the net.
|
||||
//
|
||||
// The global row is load-bearing: in ww the global struct-field load is a
|
||||
// separate codegen arm with no slice sibling (cstage folds local+global in one
|
||||
// base_reg arm; ww splits them), so a cap-drop there would hide from the
|
||||
// local-field rows — hence its own @test fn.
|
||||
//
|
||||
// Each shape POISONS the source str so cap != len (a `.cap =` pseudo-field
|
||||
// write, no malloc / no import), then observes cap through `let s: str =
|
||||
// <field>` (a 3-word copy into the slot) and `s.cap` (an N_IDENT pseudo-field
|
||||
// read off the slot). spoil() interposes a CX-clobbering call between the field
|
||||
// store and the read so a broken 2-word read observes spoil's leftover (44),
|
||||
// not a stale poison the store coincidentally left behind. .len is the control.
|
||||
|
||||
package str_field_cap_test;
|
||||
|
||||
type rec = struct { f: str };
|
||||
|
||||
let g: rec;
|
||||
|
||||
fn spoil() i32 = {
|
||||
let z: str = "zzzz";
|
||||
z.cap = 44i32;
|
||||
let w: str = z;
|
||||
return w.cap: i32;
|
||||
};
|
||||
|
||||
@test fn s1local_field() void = {
|
||||
// `st.f` direct struct local field (base BP). Poison cap=8 (len=2).
|
||||
let p: str = "hi";
|
||||
p.cap = 8i32;
|
||||
let st: rec;
|
||||
st.f = p;
|
||||
let junk: i32 = spoil();
|
||||
let s: str = st.f;
|
||||
assert(s.cap: i32 == 8);
|
||||
assert(s.len: i32 == 2);
|
||||
assert(junk == 44);
|
||||
};
|
||||
|
||||
@test fn s1global_field() void = {
|
||||
// `g.f` direct struct global field (base CX path), the no-local-sibling
|
||||
// arm in ww. Poison cap=9 (len=5).
|
||||
let p: str = "world";
|
||||
p.cap = 9i32;
|
||||
g.f = p;
|
||||
let s: str = g.f;
|
||||
assert(s.cap: i32 == 9);
|
||||
assert(s.len: i32 == 5);
|
||||
};
|
||||
|
||||
@test fn s2ptr_field() void = {
|
||||
// `pst.f` field via a *struct local (deref, base BX). Poison cap=7
|
||||
// (len=3).
|
||||
let p: str = "abc";
|
||||
p.cap = 7i32;
|
||||
let st: rec;
|
||||
st.f = p;
|
||||
let pst: *rec = &st;
|
||||
let s: str = pst.f;
|
||||
assert(s.cap: i32 == 7);
|
||||
assert(s.len: i32 == 3);
|
||||
};
|
||||
62
test/lang/str_massign_store_cap_test.ww
Normal file
62
test/lang/str_massign_store_cap_test.ww
Normal file
@@ -0,0 +1,62 @@
|
||||
// str_massign_store_cap_test — a tuple-destructure REASSIGNMENT `a, s = call()`
|
||||
// (N_MASSIGN) whose str element must write the full 24B {ptr,len,cap} header
|
||||
// into the str slot, not just {ptr}, migrated from
|
||||
// test/wcc/939_str_massign_store_cap_run.c (G3 fold). str is 24B since Phase 2
|
||||
// (#1); the N_MASSIGN arm previously stored the str element with the bare
|
||||
// scalar path (one word), dropping len/cap. cs==ww held (byte-id-blind), so
|
||||
// behavioral @test is the net.
|
||||
//
|
||||
// N_MASSIGN vs N_MLET: `let a, s = call()` is N_MLET (fresh bindings) and
|
||||
// ALREADY destructures 3-word. `a, s = call()` with a, s PRE-DECLARED is
|
||||
// N_MASSIGN (reassignment) — the arm under test. The bindings MUST be
|
||||
// pre-declared then reassigned WITHOUT `let`, or the parser emits N_MLET and
|
||||
// this arm is never reached (silent false green).
|
||||
//
|
||||
// RHS cap!=len: the returned str is a literal whose .cap is mutated DISTINCT
|
||||
// from its len. PRE-POISON: s is seeded with a DIFFERENT str (ptr='q', len=4,
|
||||
// cap=5) via `let s: str = q` — a PROVEN already-3-word let-init copy, which
|
||||
// both DECLARES s (so the comma-assign is N_MASSIGN) and poisons its slot. A
|
||||
// broken 1-word store writes only s.ptr, so the read-back observes the poison
|
||||
// len 4 / cap 5, never 2 / 8. The full-triple assert (s[0]='h'=104, len=2,
|
||||
// cap=8) plus the scalar guard (a==5) confirm the XOR branch wires both slots.
|
||||
|
||||
package str_massign_store_cap_test;
|
||||
|
||||
fn mk() (i64, str) = {
|
||||
let p: str = "hi"; p.cap = 8i32;
|
||||
return (5i64, p);
|
||||
};
|
||||
|
||||
fn mk2() (str, i64) = {
|
||||
let p: str = "hi"; p.cap = 8i32;
|
||||
return (p, 5i64);
|
||||
};
|
||||
|
||||
@test fn massign_store_str_pos1() void = {
|
||||
// A — `a, s = mk()`, str the 2nd tuple element (l1). a and s are
|
||||
// pre-declared (so the comma-assign is N_MASSIGN, not N_MLET); s is
|
||||
// poisoned (cap=5,len=4,'q') by its let-init copy. The G3 store then
|
||||
// lands the test str (cap=8,len=2,'h') via (DX,CX,R8)->s, AX->a.
|
||||
let q: str = "qqqq"; q.cap = 5i32;
|
||||
let a: i64 = 7i64;
|
||||
let s: str = q;
|
||||
a, s = mk();
|
||||
assert(s.cap: i32 == 8);
|
||||
assert(s.len: i32 == 2);
|
||||
assert(s[0] == 104u8);
|
||||
assert(a == 5);
|
||||
};
|
||||
|
||||
@test fn massign_store_str_pos0() void = {
|
||||
// B — `s, a = mk2()`, str the 1st tuple element (l0). Same poison and
|
||||
// test values; exercises the s0_is_str routing (DX,CX,R8)->s, AX->a from
|
||||
// the other XOR side.
|
||||
let q: str = "qqqq"; q.cap = 5i32;
|
||||
let s: str = q;
|
||||
let a: i64 = 7i64;
|
||||
s, a = mk2();
|
||||
assert(s.cap: i32 == 8);
|
||||
assert(s.len: i32 == 2);
|
||||
assert(s[0] == 104u8);
|
||||
assert(a == 5);
|
||||
};
|
||||
41
test/lang/str_tuple_elem_cap_test.ww
Normal file
41
test/lang/str_tuple_elem_cap_test.ww
Normal file
@@ -0,0 +1,41 @@
|
||||
// str_tuple_elem_cap_test — a tuple POSITIONAL str element read `t.N` (N_IDENT
|
||||
// base, TY_TUPLE) must load the full 24B {ptr,len,cap} header, not just
|
||||
// {ptr,len}, migrated from test/wcc/935_str_tuple_elem_cap_run.c (C4.6 S3). str
|
||||
// is 24B since Phase 2 (#1); pre-S3 the tuple str-element arm loaded 2 words
|
||||
// (ptr in AX, len in BX) and dropped cap. The miscompile was cs==ww, so the
|
||||
// 990-997 byte-id gates stayed GREEN while the runtime was wrong — behavioral
|
||||
// @test is the net. UNLIKE the field/elem arms there is no adjacent slice-
|
||||
// element arm to fold onto, so the 3-word triple is authored directly to the
|
||||
// canonical slice-header ABI (AX=ptr, BX=len, CX=cap).
|
||||
//
|
||||
// The tuple is built by a (i64, str)-returning mk() so the poisoned cap=8 rides
|
||||
// the 4-word return ABI into the tuple slot's +24 word (do NOT simplify to a
|
||||
// local tuple literal — different codegen path); `t.1` then reads it back
|
||||
// 3-word. spoil() interposes a CX-clobbering call between the build and the
|
||||
// `t.1` read so a broken 2-word read observes 44 not 8, not a stale CX.
|
||||
|
||||
package str_tuple_elem_cap_test;
|
||||
|
||||
fn mk() (i64, str) = {
|
||||
let p: str = "hi";
|
||||
p.cap = 8i32;
|
||||
return (5i64, p);
|
||||
};
|
||||
|
||||
fn spoil() i32 = {
|
||||
let z: str = "zzzz";
|
||||
z.cap = 44i32;
|
||||
let w: str = z;
|
||||
return w.cap: i32;
|
||||
};
|
||||
|
||||
@test fn tuple_positional_str_elem() void = {
|
||||
// `t.1` positional str element of a local (i64, str) tuple. Poison cap=8
|
||||
// (len=2) rides mk()'s return ABI into the str element's slot.
|
||||
let t: (i64, str) = mk();
|
||||
let junk: i32 = spoil();
|
||||
let s: str = t.1;
|
||||
assert(s.cap: i32 == 8);
|
||||
assert(s.len: i32 == 2);
|
||||
assert(junk == 44);
|
||||
};
|
||||
Reference in New Issue
Block a user