w6c+w6c_ww: tuple-let receive keyed on type classify, not producer shape (C-t1, #33)

wwstage cglet's tuple receive was producer-SHAPE-keyed: the mixed
str/scalar arm required s0_is_str != s1_is_str (syntactic) and the
rt16 arm required an N_CALL rhs (rettupleof), so a scalar-scalar tuple
LITERAL `let t: (u32,u32) = (3,4)` matched neither and fell to the
generic single-word store — word 1 silently dropped (#209/#211-class
syntactic-vs-type keying). cstage's twin arm was sz==16/32 magic-size
keyed, so 24B 3-scalar tuples dropped words 2+ on BOTH sources.

Both stages now key the same way: declared-type TY_TUPLE + in-cap
register classify (cg_sret_retsize / sretretsize == 0, the shared
SSoT), alias-peeled; the two wwstage shape arms collapse into one
type-keyed arm walking the declared element list (the #240 lesson —
never the producer's). Over-cap falls through to the sret receive
exactly as before; unannotated `let t = f()` rides inferletcalltype.

941 grows the t1 rows: lit packed/16B/3-scalar + call 3-scalar fail at
the C-t0 parent (10/39 checks — wwstage lit halves AND both-stage
24B halves), mixed-lit + unannotated-call anchor the untouched paths.

Filed while probing: cstage silently accepts an over-cap tuple-LITERAL
let where wwstage loud-stops (pre-existing at master, task #64).
This commit is contained in:
2026-06-04 18:28:30 +09:00
parent fdfc2ce318
commit 12af54f9f8
5 changed files with 237 additions and 281 deletions

View File

@@ -10499,21 +10499,28 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16)); ins2(c, A_MOVQ, areg(D_CX), amem(D_BP, off + 16));
break; break;
} }
/* Tuple initialiser from a function call (#105 / #164/#107), /* Tuple initialiser (#105 / #164/#107): every IN-CAP tuple
* 16B (two eightbytes) or 32B (scalar/float + slice/str header). * receive routes here. Each element rides its SysV class: a
* Each element rides its SysV class: a float its SSE cursor reg * float its SSE cursor reg (X0,X1 = tuple_sse_seq), an
* (X0,X1 = tuple_sse_seq), an integer/ptr word its INTEGER cursor * integer/ptr word its INTEGER cursor reg (tuple_rseq), a
* reg (tuple_rseq), a slice/str its 3-word {ptr,len,cap} header * slice/str its 3-word {ptr,len,cap} header over consecutive
* over consecutive INTEGER cursor regs — INDEPENDENT counters, * INTEGER cursor regs — INDEPENDENT counters, so the RETURN
* so the RETURN leaves floats in X0/X1 and integer words in * leaves floats in X0/X1 and integer words in AX/DX/CX/R8. A
* AX/DX/CX/R8. A blanket MOVQ spill would store garbage where a * blanket MOVQ spill would store garbage where a float rode
* float rode and the #103-FACE-Z field read (MOVSD-from-slot) * and the #103-FACE-Z field read (MOVSD-from-slot) would see
* would see it. tuple_store routes each element from its real * it. tuple_store routes each element from its real class
* class into its positional slot (eoff steps by the element's * into its positional slot (eoff steps by the element's slot
* slot size: a slice/str takes its 24B header); the same split * size: a slice/str takes its 24B header); the same split
* drives the destructure / reassign sites. */ * drives the destructure / reassign sites.
*
* C-t1 (#33 family): keyed on the TYPE's register classify
* (cg_sret_retsize == 0, the shared SSoT), not the sz==16/32
* magic — that key missed sz==24/40/48 in-cap shapes (3-scalar
* tuples dropped words 2+ silently) and pre-C-t0 missed the
* packed sz==8 entirely. Over-cap falls through to the sret
* receive below, exactly as before. */
if (n->rhs && lu && lu->kind == TY_TUPLE if (n->rhs && lu && lu->kind == TY_TUPLE
&& (sz == 16 || sz == 32)) { && cg_sret_retsize(lt) == 0) {
cgexpr(c, n->rhs, *locals); cgexpr(c, n->rhs, *locals);
int gpcur = 0, ssecur = 0, eoff = 0, ef32; int gpcur = 0, ssecur = 0, eoff = 0, ef32;
for (Tparam *p = lu->params; p; p = p->next) { for (Tparam *p = lu->params; p; p = p->next) {

View File

@@ -32169,103 +32169,63 @@ fn cglet(c: *cgen, n: *node) void = {
}; };
// fall through to the generic sret receive below. // fall through to the generic sret receive below.
}; };
// 32B tuple init for `let t: (scalar, str) = call()` / // In-cap tuple initialiser (#105 / #164/#107): every in-cap
// `let t: (str, scalar) = call()` (#105 / #164/#107). Each // tuple receive routes here, keyed on the DECLARED TYPE's
// element rides its SysV class — a float its SSE cursor reg // register classify (sretretsize == 0, the shared SSoT) —
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg // mirror of cstage cgen.c N_LET tuple arm. Each element rides
// its SysV class — a float its SSE cursor reg (X0,X1 =
// tupsse), an integer/ptr word its INTEGER cursor reg
// (tupreg), a slice/str its 3-word {ptr,len,cap} header over // (tupreg), a slice/str its 3-word {ptr,len,cap} header over
// consecutive INTEGER cursor regs — on INDEPENDENT counters. // consecutive INTEGER cursor regs — on INDEPENDENT counters.
// tupstore routes each element from its real class into its // tupstore routes each element from its real class into its
// positional slot (eoff steps by the element's slot size: a // positional slot (eoff steps by the element's slot size: a
// slice/str takes its 24B header). str IS []u8 (24B) → 32B tuple // slice/str takes its 24B header). Over-cap falls through to
// (#1/Phase 3, task #5). Mirror of the cstage unified branch. // the sret receive below (#240 — an over-cap receive via the
// register cursor read garbage past R8).
// //
// #240: cap-gate on sz (16/32 = in-cap, AX/DX/CX/R8). Without it // C-t1 (#33): the old keys were producer-SHAPE — the mixed
// an OVER-cap mixed-scalar tuple (e.g. (int,[]u8,str), 56B) was // str/scalar arm required s0_is_str != s1_is_str (syntactic)
// received here via the register cursor (4 GP regs + R8 fill) // AND sz==16/32, the rt16 arm required an N_CALL rhs
// instead of from the sret dest the callee actually wrote — a // (rettupleof) — so a scalar-scalar tuple LITERAL `(3, 4)`
// silent cs!=ww divergence (cstage gates the twin branch on // matched neither and fell to the generic single-word store,
// `sz == 16 || sz == 32`, cgen.c N_LET, and falls an over-cap // silently dropping word 1 (#209/#211-class syntactic-vs-type
// tuple through to the sret receive below). // keying). Alias-peel mirrors cstage's type_chase_named; the
if (n.lhs != nil) { // unannotated `let t = f()` shape rides the inferletcalltype
if (n.lhs.kind == nkind.N_TTUPLE) { // tn above.
let p0: *node = n.lhs.list; let ttup: *node = tn;
let p1: *node = nil; for (ttup != nil && ttup.kind == nkind.N_TNAME) {
if (p0 != nil) { p1 = p0.next; }; ttup = aliaslookup(c, ttup.str);
let p0t: *node = nil;
let p1t: *node = nil;
if (p0 != nil) { p0t = p0.lhs; };
if (p1 != nil) { p1t = p1.lhs; };
let s0_is_str: bool = isstrtype(c, p0t)
|| isslicetype(c, p0t);
let s1_is_str: bool = isstrtype(c, p1t)
|| isslicetype(c, p1t);
if (p0 != nil) {
if (p1 != nil) {
if ((s0_is_str != s1_is_str) && (sz == 16 || sz == 32)) {
cgexpr(c, rhs);
let gpcur: i32 = 0;
let ssecur: i32 = 0;
let eoff: i32 = 0;
let q: *node = n.lhs.list;
for (q != nil) {
let qt: *node = q.lhs;
let isflt: bool = isfloattype(c, qt);
let wide: bool = isstrtype(c, qt)
|| isslicetype(c, qt);
tupstore(c, gpcur, ssecur,
off + eoff, wide, qt);
if (isflt) {
ssecur = ssecur + 1;
} else {
gpcur = gpcur + tupebytes(wide);
};
if (wide) {
eoff = eoff + (tyslicesize(): i32);
} else {
eoff = eoff + 8;
};
q = q.next;
};
c.lastwasreturn = 0;
return;
};
};
};
};
}; };
// 16B tuple init from a function call (#105 / #164/#107). Each if (ttup != nil) {
// eightbyte rides its SysV class: a float its SSE cursor reg if (ttup.kind == nkind.N_TTUPLE
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg && sretretsize(c, ttup) == 0) {
// (AX,DX = tupreg), INDEPENDENT counters — the RETURN leaves cgexpr(c, rhs);
// floats in X0/X1 and integer words in AX/DX, so a blanket MOVQ let gpcur: i32 = 0;
// spill would store garbage where a float rode and the #103- let ssecur: i32 = 0;
// FACE-Z field read (MOVSD-from-slot) would see it. tupstore let eoff: i32 = 0;
// routes each word from its real class; the same split drives let q: *node = ttup.list;
// the destructure / reassign sites. Without this branch a 16B for (q != nil) {
// tuple receive fell to the generic single-word store below and let qt: *node = q.lhs;
// dropped word1 — silent loss of t.1 (#102). let isflt: bool = isfloattype(c, qt);
let rt16: *node = rettupleof(c, rhs); let wide: bool = isstrtype(c, qt)
if (rt16 != nil && sz == 16) { || isslicetype(c, qt);
cgexpr(c, rhs); tupstore(c, gpcur, ssecur,
let gpcur: i32 = 0; off + eoff, wide, qt);
let ssecur: i32 = 0; if (isflt) {
let eoff: i32 = 0; ssecur = ssecur + 1;
let q: *node = rt16.list; } else {
for (q != nil) { gpcur = gpcur + tupebytes(wide);
let qt: *node = q.lhs; };
let isflt: bool = isfloattype(c, qt); if (wide) {
tupstore(c, gpcur, ssecur, off + eoff, false, qt); eoff = eoff + (tyslicesize(): i32);
if (isflt) { } else {
ssecur = ssecur + 1; eoff = eoff + 8;
} else { };
gpcur = gpcur + 1; q = q.next;
}; };
eoff = eoff + 8; c.lastwasreturn = 0;
q = q.next; return;
}; };
c.lastwasreturn = 0;
return;
}; };
// Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T). // Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T).
// Walk elements in declaration order, store each at off + i*esz // Walk elements in declaration order, store each at off + i*esz

View File

@@ -1975,103 +1975,63 @@ fn cglet(c: *cgen, n: *node) void = {
}; };
// fall through to the generic sret receive below. // fall through to the generic sret receive below.
}; };
// 32B tuple init for `let t: (scalar, str) = call()` / // In-cap tuple initialiser (#105 / #164/#107): every in-cap
// `let t: (str, scalar) = call()` (#105 / #164/#107). Each // tuple receive routes here, keyed on the DECLARED TYPE's
// element rides its SysV class — a float its SSE cursor reg // register classify (sretretsize == 0, the shared SSoT) —
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg // mirror of cstage cgen.c N_LET tuple arm. Each element rides
// its SysV class — a float its SSE cursor reg (X0,X1 =
// tupsse), an integer/ptr word its INTEGER cursor reg
// (tupreg), a slice/str its 3-word {ptr,len,cap} header over // (tupreg), a slice/str its 3-word {ptr,len,cap} header over
// consecutive INTEGER cursor regs — on INDEPENDENT counters. // consecutive INTEGER cursor regs — on INDEPENDENT counters.
// tupstore routes each element from its real class into its // tupstore routes each element from its real class into its
// positional slot (eoff steps by the element's slot size: a // positional slot (eoff steps by the element's slot size: a
// slice/str takes its 24B header). str IS []u8 (24B) → 32B tuple // slice/str takes its 24B header). Over-cap falls through to
// (#1/Phase 3, task #5). Mirror of the cstage unified branch. // the sret receive below (#240 — an over-cap receive via the
// register cursor read garbage past R8).
// //
// #240: cap-gate on sz (16/32 = in-cap, AX/DX/CX/R8). Without it // C-t1 (#33): the old keys were producer-SHAPE — the mixed
// an OVER-cap mixed-scalar tuple (e.g. (int,[]u8,str), 56B) was // str/scalar arm required s0_is_str != s1_is_str (syntactic)
// received here via the register cursor (4 GP regs + R8 fill) // AND sz==16/32, the rt16 arm required an N_CALL rhs
// instead of from the sret dest the callee actually wrote — a // (rettupleof) — so a scalar-scalar tuple LITERAL `(3, 4)`
// silent cs!=ww divergence (cstage gates the twin branch on // matched neither and fell to the generic single-word store,
// `sz == 16 || sz == 32`, cgen.c N_LET, and falls an over-cap // silently dropping word 1 (#209/#211-class syntactic-vs-type
// tuple through to the sret receive below). // keying). Alias-peel mirrors cstage's type_chase_named; the
if (n.lhs != nil) { // unannotated `let t = f()` shape rides the inferletcalltype
if (n.lhs.kind == nkind.N_TTUPLE) { // tn above.
let p0: *node = n.lhs.list; let ttup: *node = tn;
let p1: *node = nil; for (ttup != nil && ttup.kind == nkind.N_TNAME) {
if (p0 != nil) { p1 = p0.next; }; ttup = aliaslookup(c, ttup.str);
let p0t: *node = nil;
let p1t: *node = nil;
if (p0 != nil) { p0t = p0.lhs; };
if (p1 != nil) { p1t = p1.lhs; };
let s0_is_str: bool = isstrtype(c, p0t)
|| isslicetype(c, p0t);
let s1_is_str: bool = isstrtype(c, p1t)
|| isslicetype(c, p1t);
if (p0 != nil) {
if (p1 != nil) {
if ((s0_is_str != s1_is_str) && (sz == 16 || sz == 32)) {
cgexpr(c, rhs);
let gpcur: i32 = 0;
let ssecur: i32 = 0;
let eoff: i32 = 0;
let q: *node = n.lhs.list;
for (q != nil) {
let qt: *node = q.lhs;
let isflt: bool = isfloattype(c, qt);
let wide: bool = isstrtype(c, qt)
|| isslicetype(c, qt);
tupstore(c, gpcur, ssecur,
off + eoff, wide, qt);
if (isflt) {
ssecur = ssecur + 1;
} else {
gpcur = gpcur + tupebytes(wide);
};
if (wide) {
eoff = eoff + (tyslicesize(): i32);
} else {
eoff = eoff + 8;
};
q = q.next;
};
c.lastwasreturn = 0;
return;
};
};
};
};
}; };
// 16B tuple init from a function call (#105 / #164/#107). Each if (ttup != nil) {
// eightbyte rides its SysV class: a float its SSE cursor reg if (ttup.kind == nkind.N_TTUPLE
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg && sretretsize(c, ttup) == 0) {
// (AX,DX = tupreg), INDEPENDENT counters — the RETURN leaves cgexpr(c, rhs);
// floats in X0/X1 and integer words in AX/DX, so a blanket MOVQ let gpcur: i32 = 0;
// spill would store garbage where a float rode and the #103- let ssecur: i32 = 0;
// FACE-Z field read (MOVSD-from-slot) would see it. tupstore let eoff: i32 = 0;
// routes each word from its real class; the same split drives let q: *node = ttup.list;
// the destructure / reassign sites. Without this branch a 16B for (q != nil) {
// tuple receive fell to the generic single-word store below and let qt: *node = q.lhs;
// dropped word1 — silent loss of t.1 (#102). let isflt: bool = isfloattype(c, qt);
let rt16: *node = rettupleof(c, rhs); let wide: bool = isstrtype(c, qt)
if (rt16 != nil && sz == 16) { || isslicetype(c, qt);
cgexpr(c, rhs); tupstore(c, gpcur, ssecur,
let gpcur: i32 = 0; off + eoff, wide, qt);
let ssecur: i32 = 0; if (isflt) {
let eoff: i32 = 0; ssecur = ssecur + 1;
let q: *node = rt16.list; } else {
for (q != nil) { gpcur = gpcur + tupebytes(wide);
let qt: *node = q.lhs; };
let isflt: bool = isfloattype(c, qt); if (wide) {
tupstore(c, gpcur, ssecur, off + eoff, false, qt); eoff = eoff + (tyslicesize(): i32);
if (isflt) { } else {
ssecur = ssecur + 1; eoff = eoff + 8;
} else { };
gpcur = gpcur + 1; q = q.next;
}; };
eoff = eoff + 8; c.lastwasreturn = 0;
q = q.next; return;
}; };
c.lastwasreturn = 0;
return;
}; };
// Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T). // Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T).
// Walk elements in declaration order, store each at off + i*esz // Walk elements in declaration order, store each at off + i*esz

View File

@@ -32169,103 +32169,63 @@ fn cglet(c: *cgen, n: *node) void = {
}; };
// fall through to the generic sret receive below. // fall through to the generic sret receive below.
}; };
// 32B tuple init for `let t: (scalar, str) = call()` / // In-cap tuple initialiser (#105 / #164/#107): every in-cap
// `let t: (str, scalar) = call()` (#105 / #164/#107). Each // tuple receive routes here, keyed on the DECLARED TYPE's
// element rides its SysV class — a float its SSE cursor reg // register classify (sretretsize == 0, the shared SSoT) —
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg // mirror of cstage cgen.c N_LET tuple arm. Each element rides
// its SysV class — a float its SSE cursor reg (X0,X1 =
// tupsse), an integer/ptr word its INTEGER cursor reg
// (tupreg), a slice/str its 3-word {ptr,len,cap} header over // (tupreg), a slice/str its 3-word {ptr,len,cap} header over
// consecutive INTEGER cursor regs — on INDEPENDENT counters. // consecutive INTEGER cursor regs — on INDEPENDENT counters.
// tupstore routes each element from its real class into its // tupstore routes each element from its real class into its
// positional slot (eoff steps by the element's slot size: a // positional slot (eoff steps by the element's slot size: a
// slice/str takes its 24B header). str IS []u8 (24B) → 32B tuple // slice/str takes its 24B header). Over-cap falls through to
// (#1/Phase 3, task #5). Mirror of the cstage unified branch. // the sret receive below (#240 — an over-cap receive via the
// register cursor read garbage past R8).
// //
// #240: cap-gate on sz (16/32 = in-cap, AX/DX/CX/R8). Without it // C-t1 (#33): the old keys were producer-SHAPE — the mixed
// an OVER-cap mixed-scalar tuple (e.g. (int,[]u8,str), 56B) was // str/scalar arm required s0_is_str != s1_is_str (syntactic)
// received here via the register cursor (4 GP regs + R8 fill) // AND sz==16/32, the rt16 arm required an N_CALL rhs
// instead of from the sret dest the callee actually wrote — a // (rettupleof) — so a scalar-scalar tuple LITERAL `(3, 4)`
// silent cs!=ww divergence (cstage gates the twin branch on // matched neither and fell to the generic single-word store,
// `sz == 16 || sz == 32`, cgen.c N_LET, and falls an over-cap // silently dropping word 1 (#209/#211-class syntactic-vs-type
// tuple through to the sret receive below). // keying). Alias-peel mirrors cstage's type_chase_named; the
if (n.lhs != nil) { // unannotated `let t = f()` shape rides the inferletcalltype
if (n.lhs.kind == nkind.N_TTUPLE) { // tn above.
let p0: *node = n.lhs.list; let ttup: *node = tn;
let p1: *node = nil; for (ttup != nil && ttup.kind == nkind.N_TNAME) {
if (p0 != nil) { p1 = p0.next; }; ttup = aliaslookup(c, ttup.str);
let p0t: *node = nil;
let p1t: *node = nil;
if (p0 != nil) { p0t = p0.lhs; };
if (p1 != nil) { p1t = p1.lhs; };
let s0_is_str: bool = isstrtype(c, p0t)
|| isslicetype(c, p0t);
let s1_is_str: bool = isstrtype(c, p1t)
|| isslicetype(c, p1t);
if (p0 != nil) {
if (p1 != nil) {
if ((s0_is_str != s1_is_str) && (sz == 16 || sz == 32)) {
cgexpr(c, rhs);
let gpcur: i32 = 0;
let ssecur: i32 = 0;
let eoff: i32 = 0;
let q: *node = n.lhs.list;
for (q != nil) {
let qt: *node = q.lhs;
let isflt: bool = isfloattype(c, qt);
let wide: bool = isstrtype(c, qt)
|| isslicetype(c, qt);
tupstore(c, gpcur, ssecur,
off + eoff, wide, qt);
if (isflt) {
ssecur = ssecur + 1;
} else {
gpcur = gpcur + tupebytes(wide);
};
if (wide) {
eoff = eoff + (tyslicesize(): i32);
} else {
eoff = eoff + 8;
};
q = q.next;
};
c.lastwasreturn = 0;
return;
};
};
};
};
}; };
// 16B tuple init from a function call (#105 / #164/#107). Each if (ttup != nil) {
// eightbyte rides its SysV class: a float its SSE cursor reg if (ttup.kind == nkind.N_TTUPLE
// (X0,X1 = tupsse), an integer/ptr word its INTEGER cursor reg && sretretsize(c, ttup) == 0) {
// (AX,DX = tupreg), INDEPENDENT counters — the RETURN leaves cgexpr(c, rhs);
// floats in X0/X1 and integer words in AX/DX, so a blanket MOVQ let gpcur: i32 = 0;
// spill would store garbage where a float rode and the #103- let ssecur: i32 = 0;
// FACE-Z field read (MOVSD-from-slot) would see it. tupstore let eoff: i32 = 0;
// routes each word from its real class; the same split drives let q: *node = ttup.list;
// the destructure / reassign sites. Without this branch a 16B for (q != nil) {
// tuple receive fell to the generic single-word store below and let qt: *node = q.lhs;
// dropped word1 — silent loss of t.1 (#102). let isflt: bool = isfloattype(c, qt);
let rt16: *node = rettupleof(c, rhs); let wide: bool = isstrtype(c, qt)
if (rt16 != nil && sz == 16) { || isslicetype(c, qt);
cgexpr(c, rhs); tupstore(c, gpcur, ssecur,
let gpcur: i32 = 0; off + eoff, wide, qt);
let ssecur: i32 = 0; if (isflt) {
let eoff: i32 = 0; ssecur = ssecur + 1;
let q: *node = rt16.list; } else {
for (q != nil) { gpcur = gpcur + tupebytes(wide);
let qt: *node = q.lhs; };
let isflt: bool = isfloattype(c, qt); if (wide) {
tupstore(c, gpcur, ssecur, off + eoff, false, qt); eoff = eoff + (tyslicesize(): i32);
if (isflt) { } else {
ssecur = ssecur + 1; eoff = eoff + 8;
} else { };
gpcur = gpcur + 1; q = q.next;
}; };
eoff = eoff + 8; c.lastwasreturn = 0;
q = q.next; return;
}; };
c.lastwasreturn = 0;
return;
}; };
// Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T). // Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T).
// Walk elements in declaration order, store each at off + i*esz // Walk elements in declaration order, store each at off + i*esz

View File

@@ -163,6 +163,75 @@ static const struct row rows[] = {
" if (b != 4) { return 2; };\n" " if (b != 4) { return 2; };\n"
" return 0;\n" " return 0;\n"
"};\n", 0 }, "};\n", 0 },
/* ---- C-t1 (#33): the let RECEIVE re-keyed onto the declared
* type's register classify. Pre-C-t1 wwstage keyed on producer
* SHAPE (mixed-str syntactic / rettupleof N_CALL) so a
* scalar-scalar LITERAL matched neither and dropped word 1;
* cstage keyed on sz==16/32 so 24B 3-scalar shapes dropped words
* on BOTH sources. ---- */
{ "t1_lit_packed",
"package main;\n"
"fn second() u32 = {\n"
" let t: (u32, u32) = (3, 4);\n"
" if (t.0 != 3) { return 99; };\n"
" return t.1;\n"
"};\n"
"export fn main() i32 = {\n"
" if (second() != 4) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ "t1_lit_16",
"package main;\n"
"export fn main() i32 = {\n"
" let t: (i64, i64) = (3, 4);\n"
" if (t.0 != 3) { return 1; };\n"
" if (t.1 != 4) { return 2; };\n"
" return 0;\n"
"};\n", 0 },
{ "t1_lit_mixed",
"package main;\n"
"export fn main() i32 = {\n"
" let t: (i64, str) = (12, \"wxyz\");\n"
" if (t.0 != 12) { return 1; };\n"
" if (len(t.1) != 4) { return 2; };\n"
" return 0;\n"
"};\n", 0 },
{ "t1_lit_3scalar",
"package main;\n"
"fn second() i64 = {\n"
" let t: (i64, i64, i64) = (3, 4, 5);\n"
" if (t.0 != 3) { return -1; };\n"
" if (t.1 != 4) { return -2; };\n"
" return t.2;\n"
"};\n"
"export fn main() i32 = {\n"
" if (second() != 5) { return 1; };\n"
" return 0;\n"
"};\n", 0 },
{ "t1_call_3scalar",
"package main;\n"
"fn f() (i64, i64, i64) = {\n"
" return (7, 8, 9);\n"
"};\n"
"export fn main() i32 = {\n"
" let t: (i64, i64, i64) = f();\n"
" if (t.0 != 7) { return 1; };\n"
" if (t.1 != 8) { return 2; };\n"
" if (t.2 != 9) { return 3; };\n"
" return 0;\n"
"};\n", 0 },
{ "t1_call_noannot_neutral",
"package main;\n"
"fn f() (i64, i64) = {\n"
" return (3, 4);\n"
"};\n"
"export fn main() i32 = {\n"
" let t = f();\n"
" if (t.0 != 3) { return 1; };\n"
" if (t.1 != 4) { return 2; };\n"
" return 0;\n"
"};\n", 0 },
}; };
/* build+run via a driver (ww / ww_ww); returns 0 pass, nonzero fail. */ /* build+run via a driver (ww / ww_ww); returns 0 pass, nonzero fail. */