wcc+w6c+w6c_ww: insert() builtin — single-element slice insertion (part of #35)

Hare's insert(xs[idx], v) (ref/harec/src/check.c:745
check_expr_append_insert — append/insert share the checker arm,
"insert" at :786): checker accepts an INDEX place over a slice plus
one value, stamps void; idx == len is a legal end-insert (the
ref/hare os/exec/platform_cmd.ha:86 idiom). Loud-rejects with exact
texts: spread form insert(xs[i], vs...) (filed, #35 — also covers
harec's with-length form via the arity check), range place (not
Hare; harec only parses ACCESS_INDEX, :784), non-index operands,
array bases, wrong arity. delete()-parity throughout.

Lowering (both stages, converged byte-identical by construction) is
a DESUGAR: append(xs, v) — reusing append's grow (rt_ensure) and the
entire #34 value-store dispatch (scalar / str-slice header / tagged
widen / struct fill) verbatim, one boxing choke-point — lands v at
slot len-1; then a rotate-right of [idx, len) moves it home through
a fresh per-site esz frame scratch (@insscr). The rotate is delete's
shift loop in reverse (descending j, the safe memmove-up direction)
and is a same-slice whole-stride raw byte move — no boxing exists
for any element kind. idx evaluates BEFORE the grow (Hare's
left-to-right operand order — pinned by the pregrow_len_idx row,
insert(xs[len(xs)-1], v): pre-grow [7,13,11] vs post-grow [7,11,13];
an idx==len(xs) end-insert cannot discriminate, the rotate
degenerates either way). Base shapes: local slice ident (LEAQ) and
deref-of-local ptr-to-slice (MOVQ); others rule-7 loud-stop, like
delete.

test/807: 57 fixtures — front/middle/end + idx==len via len(xs) +
the pre-grow eval-order pin, esz 1/2/4/8/16/24/56 (MOVB/MOVW/MOVL
tails, struct body, str header, 7-qword tagged from a typed local
[the regex fold-3 ha:347 newinst shape] and from a cast rvalue
[ha:419/441]), empty-slice grow, (*p)[i] deref base, front-insert
loop, 6 checker reject rows with diagnostic-text checks; every
accept row cs==ww asm byte-id.
This commit is contained in:
2026-06-04 16:50:22 +09:00
parent 48904e5ef3
commit 9861f73bbb
8 changed files with 1802 additions and 13 deletions

View File

@@ -1436,7 +1436,7 @@ cexpr(Checker *c, Node *n)
return n->type;
}
/* delete(xs[i]) — single-element slice removal, the
* delete-half of #35 (insert() stays deferred). harec
* delete-half of #35 (insert() is the twin arm below). harec
* ref/harec/src/check.c:1981-2027 also accepts the range
* form delete(xs[i..j]) (EXPR_SLICE) — out of scope here
* (regex fold-2b's consumers are all single-element);
@@ -1466,6 +1466,50 @@ cexpr(Checker *c, Node *n)
n->lhs->type = ty_err;
return n->type;
}
/* insert(xs[idx], v) — single-element slice insertion
* before idx, delete()'s twin (the insert-half of #35).
* harec models append/insert in ONE checker arm
* (ref/harec/src/check.c:745 check_expr_append_insert;
* "insert" at :786): operand 1 must be an indexing place
* over a slice; idx == len is a legal end-insert (the
* ref/hare os/exec platform_cmd.ha:86 idiom). The spread
* form insert(xs[i], vs...) and the with-length form
* (harec :821/:837) stay filed on #35 — regex fold-3's
* consumers are all single-value. A range PLACE is not
* Hare (harec asserts ACCESS_INDEX at :784; the form
* never parses there) — rejected, no task cite. */
if (n->lhs && n->lhs->kind == N_IDENT &&
n->lhs->str && strcmp(n->lhs->str, "insert") == 0) {
Node *d = n->list;
if (d == NULL || d->next == NULL ||
d->next->next != NULL)
err(c, n->pos, "insert: takes exactly two arguments");
if (d != NULL) {
(void)cexpr(c, d);
if (d->kind == N_SLICE)
err(c, n->pos, "insert: range place is invalid; operand must be an indexing expression xs[i]");
else if (d->kind != N_INDEX)
err(c, n->pos, "insert: operand must be an indexing expression xs[i]");
else {
Type *bt = d->lhs ? d->lhs->type : NULL;
while (bt && bt->kind == TY_NAMED)
bt = bt->under;
/* harec check.c:807 wording; a
* fixed-size [N]T base lands here. */
if (bt == NULL || bt->kind != TY_SLICE)
err(c, n->pos, "insert must operate on a slice");
}
if (d->next != NULL) {
if (d->next->kind == N_SPREAD)
err(c, n->pos, "insert: spread form insert(xs[i], vs...) unimplemented (task #35)");
else
(void)cexpr(c, d->next);
}
}
n->type = ty_void;
n->lhs->type = ty_err;
return n->type;
}
/* assert(cond[, msg]) / abort([msg]) — runtime checks that
* call into rt_abort. msg must be a str when present.
* Only treated as builtins when no user symbol shadows the