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

@@ -6366,15 +6366,22 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
*
* Task #30 graduated the builtin to `([]T | nomem)`. The let
* declares a bare `[]T`, so the canonical idiom wraps in `!`
* to abort on OOM; walk into the N_TRYUNW to keep the
* direct-store fast path. */
* (abort on OOM) or `?` (propagate nomem to the enclosing
* fn's tagged return). Task #45 extends the shortcut to also
* match N_TRYPROP and emit the propret pattern. */
{
Node *call = NULL;
int via_tryunw = 0;
int via_tryprop = 0;
if (n->rhs && n->rhs->kind == N_TRYUNW && n->rhs->lhs
&& n->rhs->lhs->kind == N_CALL) {
call = n->rhs->lhs;
via_tryunw = 1;
} else if (n->rhs && n->rhs->kind == N_TRYPROP
&& n->rhs->lhs
&& n->rhs->lhs->kind == N_CALL) {
call = n->rhs->lhs;
via_tryprop = 1;
}
if (call && lu && lu->kind == TY_SLICE && sz == 24
&& call->lhs && call->lhs->kind == N_IDENT
@@ -6401,6 +6408,23 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
ins2(c, A_MOVQ, aimm(60), areg(D_AX));
ins0(c, A_SYSCALL);
label(c, ok);
} else if (via_tryprop) {
/* #45: null = nomem; propagate to the
* enclosing fn's tagged return. AX = tag
* of nomem variant in cg_ret_type; epilogue
* RETs to caller. */
char *ok = mklabel(c, "tryprop_ok");
ins2(c, A_CMPQ, aimm(0), areg(D_AX));
ins1(c, A_JNE, abranch(ok));
Type *r = cg_ret_type;
if (r && r->kind == TY_NAMED) r = r->under;
int nidx = cg_tag_for_variant(r, ty_nomem);
if (nidx < 0) nidx = 1;
ins2(c, A_MOVQ, aimm(nidx), areg(D_AX));
ins2(c, A_MOVQ, areg(D_BP), areg(D_SP));
ins1(c, A_POPQ, areg(D_BP));
ins0(c, A_RET);
label(c, ok);
}
ins1(c, A_POPQ, areg(D_BX)); /* count */
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, off + 0));