cgen: #50 append/insert tagged-dst VALUE boxes PRE-grow, both stages
The #34 value-store dispatch's tagged arm ran the widen choke-point (cg_widen_tagged_store / cgwidentaggedstore — which cgexprs the value internally) AFTER cg_append_grow, so a value expression reading the destination (xs.len) saw post-grow state. Hare evaluates arguments before the call's effects; the scalar/str/slice arms already ordered value-first. insert() inherited the bug through its desugar-to-append (9861f73) — one boxing choke-point, so this is the whole fix. Box the value into a fresh per-site @apptagscr frame scratch pre-grow (zero, BP-rooted widen store), then raw-copy the finished box into the new slot post-grow. Consumer evidence: regex fold-5b's {,0} rows (reviewer-5b's mutant; ken's corrected-root matrix /tmp/ken_silent, f50v4_tagged exit 15 both stages, byte-id, gate-blind). lib/regex.ww:643-647's pre-bound split_target workaround comment GRADUATES with this commit; the comment update itself rides the next regex touch per the standing rule. test/807: 57 -> 84 fixtures. New rows: tagged_pregrow_val (f50v4 exact), scalar_pregrow_val (no-regress control), tagged_selfref_val, tagged_str_payload, tagged_regex_minrep (the {,0} shape standalone), tagged_append_pregrow_val (direct append, the fix site), tagged_realloc_selfref_loop (ken k50a: old-base read across actual rt_ensure base moves), tagged_seq_positions (ken k50b: sequenced inserts at 0/mid, each len-reading) and tagged_void_variant (ken k50c: tag-only box through the fresh scratch). Mutation ate8977a4fails exactly the five eval-order-discriminating tagged rows, both stages. The STRUCT-LITERAL value arm keeps its post-grow field-expr fill — same eval-order class, different arm; filed (ww-core #59), cited at-site, not folded.
This commit is contained in:
@@ -8012,10 +8012,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
if (el_tagged || el_struct) {
|
||||
/* #34: no register form survives rt_ensure
|
||||
* for these — grow FIRST, then fill through
|
||||
* the dst pointer (tagged: the #12 widen
|
||||
* choke-point cgexprs the value internally;
|
||||
* struct: literal fill / ident word-copy). */
|
||||
* for these. struct: grow FIRST, then fill
|
||||
* through the dst pointer (literal fill /
|
||||
* ident word-copy). tagged: #50 — the #12
|
||||
* widen choke-point cgexprs the value
|
||||
* internally, so boxing must run PRE-grow
|
||||
* (Hare's argument order: a `xs.len` read
|
||||
* in v sees the pre-append len, like the
|
||||
* scalar arm); box into a frame scratch,
|
||||
* grow, raw-copy the finished box in. */
|
||||
/* #49 (#35's single-element sibling): a
|
||||
* place-chain source (indexed field
|
||||
* `threads[i].root_capture` regex.ha:819,
|
||||
@@ -8122,16 +8127,47 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
aplace = 1;
|
||||
}
|
||||
if (el_tagged) {
|
||||
/* Fresh slot per SITE, not the
|
||||
* shared per-size scratch: the
|
||||
* box must stay live across
|
||||
* rt_ensure, and a nested
|
||||
* append inside the value
|
||||
* expression would clobber a
|
||||
* dedup'd slot (the @apphdrscr
|
||||
* rationale; #25/#31). */
|
||||
int tg_scr = local_alloc(c,
|
||||
&locals, "@apptagscr", esz,
|
||||
cg_frame);
|
||||
ins2(c, A_XORQ, areg(D_AX),
|
||||
areg(D_AX));
|
||||
for (int zk = 0; zk < esz; zk += 8)
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, tg_scr + zk));
|
||||
cg_widen_tagged_store(c, &locals,
|
||||
esub, vn, D_BP, tg_scr, esz);
|
||||
cg_append_grow(c, sn_direct, sn_off,
|
||||
sn_scr, esz);
|
||||
cg_append_slot(c, sn_direct, sn_off,
|
||||
sn_scr, esz, D_BX);
|
||||
for (int ck = 0; ck < esz; ck += 8) {
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP, tg_scr + ck),
|
||||
areg(D_AX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BX, ck));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
cg_append_grow(c, sn_direct, sn_off,
|
||||
sn_scr, esz);
|
||||
cg_append_slot(c, sn_direct, sn_off,
|
||||
sn_scr, esz, D_BX);
|
||||
if (el_tagged) {
|
||||
cg_widen_tagged_store(c, &locals,
|
||||
esub, vn, D_BX, 0, esz);
|
||||
continue;
|
||||
}
|
||||
if (vn->kind == N_STRUCTLIT) {
|
||||
/* #59 (#50's eval-order kin):
|
||||
* the literal's field exprs
|
||||
* still eval POST-grow here —
|
||||
* filed, not folded. */
|
||||
if (cg_appendscr == 0)
|
||||
cg_appendscr = local_alloc(c,
|
||||
&locals, "@appendscr", 8,
|
||||
|
||||
@@ -26243,10 +26243,14 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
continue;
|
||||
};
|
||||
if (eltagged || elstruct) {
|
||||
// #34: no register form survives rt_ensure for these —
|
||||
// grow FIRST, then fill through the dst pointer
|
||||
// (tagged: the #12 widen choke-point cgexprs the value
|
||||
// internally; struct: literal fill / ident word-copy).
|
||||
// #34: no register form survives rt_ensure for these.
|
||||
// struct: grow FIRST, then fill through the dst pointer
|
||||
// (literal fill / ident word-copy). tagged: #50 — the
|
||||
// #12 widen choke-point cgexprs the value internally,
|
||||
// so boxing must run PRE-grow (Hare's argument order:
|
||||
// a `xs.len` read in v sees the pre-append len, like
|
||||
// the scalar arm); box into a frame scratch, grow,
|
||||
// raw-copy the finished box in.
|
||||
// #49 (#35's single-element sibling): a place-chain
|
||||
// source (indexed field `threads[i].root_capture`
|
||||
// regex.ha:819, deref spine, computed index) SPLITS
|
||||
@@ -26372,14 +26376,43 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
};
|
||||
aplace = true;
|
||||
};
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
if (eltagged) {
|
||||
cgwidentaggedstore(c, esubti, vn, "BX", 0, esz);
|
||||
// Fresh slot per SITE, not the shared per-size
|
||||
// scratch: the box must stay live across
|
||||
// rt_ensure, and a nested append inside the
|
||||
// value expression would clobber a dedup'd
|
||||
// slot (the @apphdrscr rationale; #25/#31).
|
||||
let tgscr: i32 = localalloc(c, "@apptagscr", esz, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let zk: i32 = 0;
|
||||
for (zk < esz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((tgscr + zk): i64);
|
||||
emitline("(BP)\n");
|
||||
zk += 8;
|
||||
};
|
||||
cgwidentaggedstore(c, esubti, vn, "BP", tgscr, esz);
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
let ck: i32 = 0;
|
||||
for (ck < esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((tgscr + ck): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(ck: i64, "BX");
|
||||
emitline("\n");
|
||||
ck += 8;
|
||||
};
|
||||
vn = vn.next;
|
||||
continue;
|
||||
};
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
if (vn.kind == nkind.N_STRUCTLIT) {
|
||||
// #59 (#50's eval-order kin): the literal's field
|
||||
// exprs still eval POST-grow here — filed, not
|
||||
// folded.
|
||||
let scroff: i32 = localadd(c, "@appendscr", 8, nil);
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(scroff: i64);
|
||||
|
||||
@@ -5399,10 +5399,14 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
continue;
|
||||
};
|
||||
if (eltagged || elstruct) {
|
||||
// #34: no register form survives rt_ensure for these —
|
||||
// grow FIRST, then fill through the dst pointer
|
||||
// (tagged: the #12 widen choke-point cgexprs the value
|
||||
// internally; struct: literal fill / ident word-copy).
|
||||
// #34: no register form survives rt_ensure for these.
|
||||
// struct: grow FIRST, then fill through the dst pointer
|
||||
// (literal fill / ident word-copy). tagged: #50 — the
|
||||
// #12 widen choke-point cgexprs the value internally,
|
||||
// so boxing must run PRE-grow (Hare's argument order:
|
||||
// a `xs.len` read in v sees the pre-append len, like
|
||||
// the scalar arm); box into a frame scratch, grow,
|
||||
// raw-copy the finished box in.
|
||||
// #49 (#35's single-element sibling): a place-chain
|
||||
// source (indexed field `threads[i].root_capture`
|
||||
// regex.ha:819, deref spine, computed index) SPLITS
|
||||
@@ -5528,14 +5532,43 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
};
|
||||
aplace = true;
|
||||
};
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
if (eltagged) {
|
||||
cgwidentaggedstore(c, esubti, vn, "BX", 0, esz);
|
||||
// Fresh slot per SITE, not the shared per-size
|
||||
// scratch: the box must stay live across
|
||||
// rt_ensure, and a nested append inside the
|
||||
// value expression would clobber a dedup'd
|
||||
// slot (the @apphdrscr rationale; #25/#31).
|
||||
let tgscr: i32 = localalloc(c, "@apptagscr", esz, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let zk: i32 = 0;
|
||||
for (zk < esz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((tgscr + zk): i64);
|
||||
emitline("(BP)\n");
|
||||
zk += 8;
|
||||
};
|
||||
cgwidentaggedstore(c, esubti, vn, "BP", tgscr, esz);
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
let ck: i32 = 0;
|
||||
for (ck < esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((tgscr + ck): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(ck: i64, "BX");
|
||||
emitline("\n");
|
||||
ck += 8;
|
||||
};
|
||||
vn = vn.next;
|
||||
continue;
|
||||
};
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
if (vn.kind == nkind.N_STRUCTLIT) {
|
||||
// #59 (#50's eval-order kin): the literal's field
|
||||
// exprs still eval POST-grow here — filed, not
|
||||
// folded.
|
||||
let scroff: i32 = localadd(c, "@appendscr", 8, nil);
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(scroff: i64);
|
||||
|
||||
@@ -26243,10 +26243,14 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
continue;
|
||||
};
|
||||
if (eltagged || elstruct) {
|
||||
// #34: no register form survives rt_ensure for these —
|
||||
// grow FIRST, then fill through the dst pointer
|
||||
// (tagged: the #12 widen choke-point cgexprs the value
|
||||
// internally; struct: literal fill / ident word-copy).
|
||||
// #34: no register form survives rt_ensure for these.
|
||||
// struct: grow FIRST, then fill through the dst pointer
|
||||
// (literal fill / ident word-copy). tagged: #50 — the
|
||||
// #12 widen choke-point cgexprs the value internally,
|
||||
// so boxing must run PRE-grow (Hare's argument order:
|
||||
// a `xs.len` read in v sees the pre-append len, like
|
||||
// the scalar arm); box into a frame scratch, grow,
|
||||
// raw-copy the finished box in.
|
||||
// #49 (#35's single-element sibling): a place-chain
|
||||
// source (indexed field `threads[i].root_capture`
|
||||
// regex.ha:819, deref spine, computed index) SPLITS
|
||||
@@ -26372,14 +26376,43 @@ fn cgappend(c: *cgen, n: *node) void = {
|
||||
};
|
||||
aplace = true;
|
||||
};
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
if (eltagged) {
|
||||
cgwidentaggedstore(c, esubti, vn, "BX", 0, esz);
|
||||
// Fresh slot per SITE, not the shared per-size
|
||||
// scratch: the box must stay live across
|
||||
// rt_ensure, and a nested append inside the
|
||||
// value expression would clobber a dedup'd
|
||||
// slot (the @apphdrscr rationale; #25/#31).
|
||||
let tgscr: i32 = localalloc(c, "@apptagscr", esz, nil);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
let zk: i32 = 0;
|
||||
for (zk < esz) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((tgscr + zk): i64);
|
||||
emitline("(BP)\n");
|
||||
zk += 8;
|
||||
};
|
||||
cgwidentaggedstore(c, esubti, vn, "BP", tgscr, esz);
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
let ck: i32 = 0;
|
||||
for (ck < esz) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((tgscr + ck): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(ck: i64, "BX");
|
||||
emitline("\n");
|
||||
ck += 8;
|
||||
};
|
||||
vn = vn.next;
|
||||
continue;
|
||||
};
|
||||
cgappendgrow(c, sndirect, sn_off, snscr, esz);
|
||||
cgappendslot(c, sndirect, sn_off, snscr, esz, "BX");
|
||||
if (vn.kind == nkind.N_STRUCTLIT) {
|
||||
// #59 (#50's eval-order kin): the literal's field
|
||||
// exprs still eval POST-grow here — filed, not
|
||||
// folded.
|
||||
let scroff: i32 = localadd(c, "@appendscr", 8, nil);
|
||||
emitline("\tMOVQ\tBX, ");
|
||||
emitoff(scroff: i64);
|
||||
|
||||
@@ -56,6 +56,28 @@
|
||||
* insert_in_loop | front-insert 1,2,3,4 -> [4,3,2,1] — | 47
|
||||
* | len bookkeeping under iteration + |
|
||||
* | per-position checks |
|
||||
* tagged_pregrow_val | #50 value eval-order pin, tagged | 50
|
||||
* | dst: v = (xs.len: size)+2 reads the |
|
||||
* | PRE-grow len (post-grow boxing read 5) |
|
||||
* scalar_pregrow_val | #50 scalar control: value-first | 55
|
||||
* | order was already Hare-correct |
|
||||
* tagged_selfref_val | #50: v = xs[1], an element of the | 51
|
||||
* | dst — pre-grow boxing reads OLD base |
|
||||
* tagged_str_payload | #50: str-variant box runs pre-grow | 52
|
||||
* tagged_regex_minrep | #50: the regex {,0} fold-5b shape, | 53
|
||||
* | len-reading value cast to a named |
|
||||
* | variant alias (regex.ww:643-650) |
|
||||
* tagged_realloc_selfref_loop | #50 ken k50a: 30-append | 56
|
||||
* | self-ref loop, value reads xs[0] across|
|
||||
* | actual rt_ensure base moves |
|
||||
* tagged_append_pregrow_val | #50 direct append() pin (the | 54
|
||||
* | fix site; insert inherits via desugar) |
|
||||
* tagged_seq_positions | #50 ken k50b: sequenced inserts at | 57
|
||||
* | 0 then mid, both len-reading values, |
|
||||
* | full final-order check [200,10,4,20] |
|
||||
* tagged_void_variant | #50 ken k50c: void box (tag-only) | 58
|
||||
* | through @apptagscr, then a len-reading |
|
||||
* | size insert over the mixed slice |
|
||||
*
|
||||
* Wants stay under 256 (the exit-status byte); the if-ladder rows
|
||||
* return a distinct small failure code per check, so a wrong element
|
||||
@@ -305,7 +327,7 @@ static const struct row rows[] = {
|
||||
|
||||
/* CAST-rvalue value into a tagged slice — the regex ha:419/441
|
||||
* shape `insert(insts[term_start_idx], after_idx: inst_split)`;
|
||||
* append's widen choke-point boxes it post-grow. */
|
||||
* append's widen choke-point boxes it PRE-grow (#50). */
|
||||
{ "tagged_cast",
|
||||
"package main;\n"
|
||||
"type inst_lit = rune;\n"
|
||||
@@ -370,6 +392,217 @@ static const struct row rows[] = {
|
||||
"};\n",
|
||||
47, NULL },
|
||||
|
||||
/* #50 THE value eval-order pin for the TAGGED-dst arm (ken's
|
||||
* f50v4_tagged shape exact): v = (xs.len: size)+2 widens into
|
||||
* the box. Pre-grow len=2 -> v=4; the pre-#50 post-grow boxing
|
||||
* read len=3 -> v=5 (the exit-15 path). The scalar arm always
|
||||
* ordered value-first (scalar_pregrow_val below); #50 aligns
|
||||
* the boxing arm to it. */
|
||||
{ "tagged_pregrow_val",
|
||||
"package main;\n"
|
||||
"type un = (size | void);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet xs: []un = [];\n"
|
||||
"\tappend(xs, 10: size);\n"
|
||||
"\tappend(xs, 20: size);\n"
|
||||
"\tinsert(xs[1], ((xs.len: size) + 2));\n"
|
||||
"\tmatch (xs[1]) {\n"
|
||||
"\tcase let v: size => { if (v != 4) { return (v: i32) + 10; }; };\n"
|
||||
"\tcase void => { return 2; };\n"
|
||||
"\t};\n"
|
||||
"\tif (len(xs) != 3) { return 3; };\n"
|
||||
"\treturn 50;\n"
|
||||
"};\n",
|
||||
50, NULL },
|
||||
|
||||
/* #50 scalar no-regress control (ken's f50_insertorder): the
|
||||
* scalar-dst arm was Hare-correct pre-#50 — value reads the
|
||||
* pre-grow len. */
|
||||
{ "scalar_pregrow_val",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet xs: []size = [];\n"
|
||||
"\tappend(xs, 10: size);\n"
|
||||
"\tappend(xs, 20: size);\n"
|
||||
"\tinsert(xs[1], len(xs): size);\n"
|
||||
"\tif (xs[1] != 2) { return 1; };\n"
|
||||
"\tif (len(xs) != 3) { return 2; };\n"
|
||||
"\treturn 55;\n"
|
||||
"};\n",
|
||||
55, NULL },
|
||||
|
||||
/* #50 SELF-REFERENCE value: v is an element of the destination
|
||||
* itself — pre-grow boxing must read the OLD base (and survive
|
||||
* a rt_ensure realloc via the frame scratch): [10,20] ->
|
||||
* insert(xs[0], xs[1]) -> [20,10,20]. */
|
||||
{ "tagged_selfref_val",
|
||||
"package main;\n"
|
||||
"type un = (size | void);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet xs: []un = [];\n"
|
||||
"\tappend(xs, 10: size);\n"
|
||||
"\tappend(xs, 20: size);\n"
|
||||
"\tinsert(xs[0], xs[1]);\n"
|
||||
"\tmatch (xs[0]) {\n"
|
||||
"\tcase let v: size => { if (v != 20) { return 1; }; };\n"
|
||||
"\tcase void => { return 2; };\n"
|
||||
"\t};\n"
|
||||
"\tmatch (xs[1]) {\n"
|
||||
"\tcase let v: size => { if (v != 10) { return 3; }; };\n"
|
||||
"\tcase void => { return 4; };\n"
|
||||
"\t};\n"
|
||||
"\tmatch (xs[2]) {\n"
|
||||
"\tcase let v: size => { if (v != 20) { return 5; }; };\n"
|
||||
"\tcase void => { return 6; };\n"
|
||||
"\t};\n"
|
||||
"\tif (len(xs) != 3) { return 7; };\n"
|
||||
"\treturn 51;\n"
|
||||
"};\n",
|
||||
51, NULL },
|
||||
|
||||
/* #50 str-payload tagged box: the widen store's str branch
|
||||
* (AX=ptr BX=len) also runs pre-grow now — header lands
|
||||
* whole. */
|
||||
{ "tagged_str_payload",
|
||||
"package main;\n"
|
||||
"type su = (str | void);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet ss: []su = [];\n"
|
||||
"\tappend(ss, \"aa\");\n"
|
||||
"\tinsert(ss[0], \"bb\");\n"
|
||||
"\tmatch (ss[0]) {\n"
|
||||
"\tcase let s: str => {\n"
|
||||
"\t\tif (s.len != 2) { return 1; };\n"
|
||||
"\t\tif (s[0] != 'b') { return 2; };\n"
|
||||
"\t};\n"
|
||||
"\tcase void => { return 3; };\n"
|
||||
"\t};\n"
|
||||
"\tmatch (ss[1]) {\n"
|
||||
"\tcase let s: str => { if (s[0] != 'a') { return 4; }; };\n"
|
||||
"\tcase void => { return 5; };\n"
|
||||
"\t};\n"
|
||||
"\treturn 52;\n"
|
||||
"};\n",
|
||||
52, NULL },
|
||||
|
||||
/* #50 the regex {,0}-class shape standalone (regex.ww:643-650,
|
||||
* the fold-5b consumer reviewer-5b's mutant killed): a
|
||||
* len-reading value CAST to a named variant alias —
|
||||
* `((insts.len: size) + 2): inst_split`. Pre-grow len=2 -> 4. */
|
||||
{ "tagged_regex_minrep",
|
||||
"package main;\n"
|
||||
"type ispl = size;\n"
|
||||
"type un = (ispl | void);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet xs: []un = [];\n"
|
||||
"\tappend(xs, 7: ispl);\n"
|
||||
"\tappend(xs, 8: ispl);\n"
|
||||
"\tinsert(xs[0], ((xs.len: size) + 2): ispl);\n"
|
||||
"\tmatch (xs[0]) {\n"
|
||||
"\tcase let v: ispl => { if (v != 4) { return (v: i32) + 10; }; };\n"
|
||||
"\tcase void => { return 2; };\n"
|
||||
"\t};\n"
|
||||
"\tif (len(xs) != 3) { return 3; };\n"
|
||||
"\treturn 53;\n"
|
||||
"};\n",
|
||||
53, NULL },
|
||||
|
||||
/* #50 ken's k50a: 30 appends force repeated rt_ensure realloc;
|
||||
* every value reads xs[0] (as-unwrap + arithmetic) off the
|
||||
* potentially-moved base — the one shape that pins the OLD-base
|
||||
* read across an ACTUAL base move, which single-grow rows
|
||||
* can't. */
|
||||
{ "tagged_realloc_selfref_loop",
|
||||
"package main;\n"
|
||||
"type un = (void | size);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet xs: []un = [];\n"
|
||||
"\tappend(xs, 100: size);\n"
|
||||
"\tlet i: size = 0;\n"
|
||||
"\tfor (i < 30) {\n"
|
||||
"\t\tappend(xs, ((xs[0] as size) + i));\n"
|
||||
"\t\ti += 1;\n"
|
||||
"\t};\n"
|
||||
"\tif (len(xs) != 31) { return 1; };\n"
|
||||
"\tlet k: size = 1;\n"
|
||||
"\tfor (k < 31) {\n"
|
||||
"\t\tmatch (xs[k]) {\n"
|
||||
"\t\tcase let v: size => { if (v != 100 + (k - 1)) { return 2; }; };\n"
|
||||
"\t\tcase void => { return 3; };\n"
|
||||
"\t\t};\n"
|
||||
"\t\tk += 1;\n"
|
||||
"\t};\n"
|
||||
"\treturn 56;\n"
|
||||
"};\n",
|
||||
56, NULL },
|
||||
|
||||
/* #50 direct append() (no insert desugar) — the fix lives in
|
||||
* append's tagged arm, so pin it without the rotate. */
|
||||
{ "tagged_append_pregrow_val",
|
||||
"package main;\n"
|
||||
"type un = (size | void);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet xs: []un = [];\n"
|
||||
"\tappend(xs, 10: size);\n"
|
||||
"\tappend(xs, 20: size);\n"
|
||||
"\tappend(xs, (xs.len: size) + 2);\n"
|
||||
"\tmatch (xs[2]) {\n"
|
||||
"\tcase let v: size => { if (v != 4) { return (v: i32) + 10; }; };\n"
|
||||
"\tcase void => { return 2; };\n"
|
||||
"\t};\n"
|
||||
"\tif (len(xs) != 3) { return 3; };\n"
|
||||
"\treturn 54;\n"
|
||||
"};\n",
|
||||
54, NULL },
|
||||
|
||||
/* #50 ken's k50b: SEQUENCED inserts at position edges (0, then
|
||||
* mid), each with a len-reading value — every boxing must see
|
||||
* its own pre-grow len (2 -> 200, then 3 -> 4), and the second
|
||||
* insert's rotate must shift the first's result correctly:
|
||||
* [10,20] -> [200,10,20] -> [200,10,4,20]. */
|
||||
{ "tagged_seq_positions",
|
||||
"package main;\n"
|
||||
"type un = (void | size);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet xs: []un = [];\n"
|
||||
"\tappend(xs, 10: size);\n"
|
||||
"\tappend(xs, 20: size);\n"
|
||||
"\tinsert(xs[0], ((xs.len: size) * 100));\n"
|
||||
"\tmatch (xs[0]) {\n"
|
||||
"\tcase let v: size => { if (v != 200) { return 1; }; };\n"
|
||||
"\tcase void => { return 2; };\n"
|
||||
"\t};\n"
|
||||
"\tinsert(xs[2], ((xs.len: size) + 1));\n"
|
||||
"\tmatch (xs[2]) {\n"
|
||||
"\tcase let v: size => { if (v != 4) { return 3; }; };\n"
|
||||
"\tcase void => { return 4; };\n"
|
||||
"\t};\n"
|
||||
"\tmatch (xs[1]) { case let v: size => { if (v != 10) { return 5; }; }; case void => { return 6; }; };\n"
|
||||
"\tmatch (xs[3]) { case let v: size => { if (v != 20) { return 7; }; }; case void => { return 8; }; };\n"
|
||||
"\tif (len(xs) != 4) { return 9; };\n"
|
||||
"\treturn 57;\n"
|
||||
"};\n",
|
||||
57, NULL },
|
||||
|
||||
/* #50 ken's k50c: VOID-variant value — a tag-only box through
|
||||
* the fresh @apptagscr (the zeroed scratch IS the payload),
|
||||
* then a len-reading size insert over the mixed slice. */
|
||||
{ "tagged_void_variant",
|
||||
"package main;\n"
|
||||
"type un = (void | size);\n"
|
||||
"export fn main() i32 = {\n"
|
||||
"\tlet xs: []un = [];\n"
|
||||
"\tappend(xs, 5: size);\n"
|
||||
"\tinsert(xs[0], void);\n"
|
||||
"\tif (!(xs[0] is void)) { return 1; };\n"
|
||||
"\tmatch (xs[1]) { case let v: size => { if (v != 5) { return 2; }; }; case void => { return 3; }; };\n"
|
||||
"\tinsert(xs[1], ((xs.len: size)));\n"
|
||||
"\tmatch (xs[1]) { case let v: size => { if (v != 2) { return 4; }; }; case void => { return 5; }; };\n"
|
||||
"\tif (len(xs) != 3) { return 6; };\n"
|
||||
"\treturn 58;\n"
|
||||
"};\n",
|
||||
58, NULL },
|
||||
|
||||
{ "reject_array",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
|
||||
Reference in New Issue
Block a user