cmd+selfhost+test: relax alloc-slice element-type pin via LHS retype

`alloc([], n)` synthesizes ([]u8 | nomem) at expression level — that's
fine, since the slice form only legitimately appears in let-init
position where the LHS carries the real element type. In clet, after
type-checking the rhs, peel any N_TRYPROP/N_TRYUNW wrapper, match the
alloc-slice AST shape with the same-module shadow gate (from #23),
and retype the call's tagged return to ([]T | nomem) where T is the
declared LHS element. Then assignability sees []T vs []T and accepts.

Cgen N_LET shortcut gains a viatryprop arm next to the existing
viatryunw — on rt_alloc returning null, emits the tagged-return
nomem propagation (MOVQ $nidx, AX; epilogue) instead of exit(1).
nidx comes from cg_tag_for_variant on the enclosing fn's return type,
matching the existing TRYPROP propret path.

Wwstage mirrors all four hunks (check.ww + cgenstmt.ww). Promotes the
previously-silent conf=false skip into a confident accept.

Unblocks #6 (dupall) and lays the path for #4/#7. Byte-identity
holds modulo the pre-existing #44 alloc/rt_alloc symbol divergence.
This commit is contained in:
2026-05-20 01:09:16 +09:00
parent 30a0856fe5
commit 4d4ad36b70
9 changed files with 395 additions and 41 deletions

View File

@@ -1494,6 +1494,48 @@ clet(Checker *c, Node *n)
break;
}
}
/* #45: alloc([], n) defers element type to the let-init context
* (Hare-style). cexpr's alloc-slice branch synthesizes
* ([]u8 | nomem) with no LHS context; when the let declares []T,
* retype the inner call (and any ?/! wrapper) to ([]T | nomem) /
* []T so the assignability check below succeeds for any T. cgen
* already drives element size from declared->sub at the N_LET
* shortcut (cmd/w6c/cgen.c). */
if (declared && declared->kind == TY_SLICE && declared->sub
&& declared->sub != ty_u8 && n->rhs) {
Node *wrap = NULL;
Node *call = n->rhs;
if (call->kind == N_TRYPROP || call->kind == N_TRYUNW) {
wrap = call;
call = call->lhs;
}
if (call && call->kind == N_CALL && call->lhs
&& call->lhs->kind == N_IDENT
&& call->lhs->type == ty_err
&& call->lhs->str
&& strcmp(call->lhs->str, "alloc") == 0
&& call->list && call->list->kind == N_ARRLIT
&& call->list->list == NULL
&& call->list->next
&& call->list->next->next == NULL) {
Type *st = type_slice(c->a, declared->sub);
Type *tt = newtype(c->a, TY_TAGGED);
Tparam *vs = amalloc(c->a, sizeof *vs);
Tparam *ve = amalloc(c->a, sizeof *ve);
vs->type = st; vs->next = ve;
ve->type = ty_nomem; ve->next = NULL;
tt->params = vs;
tt->size = 32;
tt->align = 8;
call->type = tt;
if (wrap) {
wrap->type = st;
initt = st;
} else {
initt = tt;
}
}
}
if (declared && initt && initt != ty_err && !has_arr_repeat &&
!type_assignable(declared, initt))
err(c, n->pos, "init %s not assignable to declared %s",