From 5f15eb3d094c749213939a883d60005d8edbe7b2 Mon Sep 17 00:00:00 2001 From: Hojun-Cho Date: Thu, 4 Jun 2026 03:42:15 +0900 Subject: [PATCH] w6c+w6c_ww: tagged widen-store struct-literal slice field keeps the 3-word header The cg_widen_tagged_store / cgwidentaggedstorebp struct-payload arm gated the 3-word {ptr,len,cap} field store on TY_STR only; a slice- typed field inside a union-payload struct literal fell to the scalar tail and silently dropped .len/.cap (the #24 gap's widener twin, both stages symmetric so byte-id gates were blind). Surfaced by #38's regex-shaped consumer: wide{xs: []u8, ys: []u8, n} widened into (wide | error | nomem). --- cmd/w6c/cgen.c | 13 ++++++++++--- selfhost/cmd/w6c/main.combined.ww | 13 +++++++++---- selfhost/cmd/wcc/cgenutil.ww | 13 +++++++++---- selfhost/cmd/wwdump/main.combined.ww | 13 +++++++++---- 4 files changed, 37 insertions(+), 15 deletions(-) diff --git a/cmd/w6c/cgen.c b/cmd/w6c/cgen.c index c4beca5b..b3bfccb3 100644 --- a/cmd/w6c/cgen.c +++ b/cmd/w6c/cgen.c @@ -2343,9 +2343,16 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src, } Type *fu = (ftype && ftype->kind == TY_NAMED) ? ftype->under : ftype; - if (fu && fu->kind == TY_STR) { - /* str IS []u8: 3-word field (ptr,len,cap) - * from cgexpr's AX/BX/CX (#1/Phase 3). */ + /* str IS []u8 and a slice is the same 3-word + * {ptr,len,cap} header from cgexpr's AX/BX/CX + * (#1/Phase 3). The slice arm rides #38's + * regex-shaped consumer (slice fields inside a + * union-payload struct literal); the prior + * str-only gate dropped .len/.cap via the + * scalar store below — the #24 gap's widener + * twin. */ + if (fu && (fu->kind == TY_STR + || fu->kind == TY_SLICE)) { ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, write_off + 8 + (int)foff + 0)); ins2(c, A_MOVQ, areg(D_BX), diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 3ba6796e..00555080 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -18899,10 +18899,15 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s emitline("\tX0, "); emitoff((slot_off + 8 + fi.foff): i64); emitline("(BP)\n"); - } else { if (isstrtype(c, fi.tnode)) { - // str IS []u8: 3-word field - // (ptr,len,cap) from cgexpr's - // AX/BX/CX (#1/Phase 3). + } else { if (isstrtype(c, fi.tnode) + || isslicetype(c, fi.tnode)) { + // str IS []u8 and a slice is the + // same 3-word {ptr,len,cap} header + // from cgexpr's AX/BX/CX (#1/Phase + // 3). Slice arm rides #38's regex- + // shaped consumer — the prior str- + // only gate dropped .len/.cap (#24 + // gap's widener twin). emitline("\tMOVQ\tAX, "); emitoff((slot_off + 8 + fi.foff): i64); emitline("(BP)\n"); diff --git a/selfhost/cmd/wcc/cgenutil.ww b/selfhost/cmd/wcc/cgenutil.ww index 11d201ea..bfc0cea1 100644 --- a/selfhost/cmd/wcc/cgenutil.ww +++ b/selfhost/cmd/wcc/cgenutil.ww @@ -3207,10 +3207,15 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s emitline("\tX0, "); emitoff((slot_off + 8 + fi.foff): i64); emitline("(BP)\n"); - } else { if (isstrtype(c, fi.tnode)) { - // str IS []u8: 3-word field - // (ptr,len,cap) from cgexpr's - // AX/BX/CX (#1/Phase 3). + } else { if (isstrtype(c, fi.tnode) + || isslicetype(c, fi.tnode)) { + // str IS []u8 and a slice is the + // same 3-word {ptr,len,cap} header + // from cgexpr's AX/BX/CX (#1/Phase + // 3). Slice arm rides #38's regex- + // shaped consumer — the prior str- + // only gate dropped .len/.cap (#24 + // gap's widener twin). emitline("\tMOVQ\tAX, "); emitoff((slot_off + 8 + fi.foff): i64); emitline("(BP)\n"); diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index a002f5d3..2ca63244 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -18899,10 +18899,15 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s emitline("\tX0, "); emitoff((slot_off + 8 + fi.foff): i64); emitline("(BP)\n"); - } else { if (isstrtype(c, fi.tnode)) { - // str IS []u8: 3-word field - // (ptr,len,cap) from cgexpr's - // AX/BX/CX (#1/Phase 3). + } else { if (isstrtype(c, fi.tnode) + || isslicetype(c, fi.tnode)) { + // str IS []u8 and a slice is the + // same 3-word {ptr,len,cap} header + // from cgexpr's AX/BX/CX (#1/Phase + // 3). Slice arm rides #38's regex- + // shaped consumer — the prior str- + // only gate dropped .len/.cap (#24 + // gap's widener twin). emitline("\tMOVQ\tAX, "); emitoff((slot_off + 8 + fi.foff): i64); emitline("(BP)\n");