wcc/cgen: #117 const slice-of-(str,*fn) DATA + &fn->DATAR reloc (both-stage)

This commit is contained in:
2026-06-06 20:27:43 +09:00
parent df1928182e
commit 942abf0482
6 changed files with 889 additions and 106 deletions

View File

@@ -38944,6 +38944,49 @@ export fn letpreintern(c: *cgen, file: *node) void = {
// #87: tagged global with a str/slice-variant literal init —
// pre-intern so emittaggeddata's DATAR (ptr@+8) finds its _S_
// rodata row (the #48 tuple-arm pattern; cstage letpreintern twin).
// #117: slice-of-tuple global — pre-intern each row's
// str-element literals in row-then-element order so
// emitslicedata's per-row DATAR patches find their _S_
// rodata rows (cstage letpreintern twin). Bounded to
// inline N_TTUPLE element types.
if (!handled && r != nil && d.lhs != nil) {
if (r.kind == nkind.N_ARRLIT
&& d.lhs.kind == nkind.N_TSLICE) {
let tupnode: *node = d.lhs.lhs;
if (tupnode != nil && tupnode.kind == nkind.N_TTUPLE) {
handled = true;
let row: *node = r.list;
for (row != nil) {
let rw: *node = row;
for (rw != nil && rw.kind == nkind.N_CAST) {
rw = rw.lhs;
};
if (rw != nil && rw.kind == nkind.N_TUPLE) {
let tp: *node = tupnode.list;
let e: *node = rw.list;
for (e != nil && tp != nil) {
let et: *node = tp.lhs;
let ev: *node = e;
for (ev != nil && ev.kind == nkind.N_CAST) {
ev = ev.lhs;
};
if (ev != nil) {
if (ev.kind == nkind.N_STRLIT
&& (isstrtype(c, et) || isslicetype(c, et))) {
if (ev.str.len > 0) {
internstrlit(c, ev.str);
};
};
};
e = e.next;
tp = tp.next;
};
};
row = row.next;
};
};
};
};
if (!handled && r != nil && d.lhs != nil) {
let tlt: *node = d.lhs;
for (tlt != nil && tlt.kind == nkind.N_TNAME) {
@@ -39684,7 +39727,7 @@ fn emitarraydata(c: *cgen, directive: str, name: str, module: str,
// and slice-of-{str,slice,tagged} elements (per-element relocs / #17)
// all loud-stop (rule 7, #10 follow-ups).
fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
rhs: *node) void = {
sltnode: *node, rhs: *node) void = {
let su: *tinfo = slt;
su = tichase(su);
// Defensive, mirrors cstage emit_slice_data's
@@ -39696,14 +39739,6 @@ fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
let etype: *tinfo = su.sub;
let eu: *tinfo = etype;
eu = tichase(eu);
if (eu != nil) {
if (eu.kind == tykind.TY_STR || eu.kind == tykind.TY_SLICE
|| eu.kind == tykind.TY_TAGGED) {
let m: str = "emitslicedata: slice-of-{str,slice,tagged} literal static-init unsupported (#10 follow-up, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
// Count elements; reject `...` (a slice literal has no target N).
let k: i32 = 0;
let e: *node = rhs.list;
@@ -39718,23 +39753,84 @@ fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
k += 1;
e = e.next;
};
let esz: i32 = etype.size: i32;
// Synthesize [k]T to ride the emitarraylitbytes choke-point.
let arrt: *tinfo = newtype(tykind.TY_ARRAY);
arrt.sub = etype;
arrt.alen = k: u64;
arrt.size = (k * esz): u64;
if (!emitarraylitbytes(c, arrt, rhs, 0)) {
let m: str = "emitslicedata: slice-literal element not a foldable constant (#10, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
// #117 aggregate-element arm: a slice of inline (str,*fn)-style
// TUPLE rows. The element type-AST node (sltnode.lhs = N_TTUPLE)
// drives the per-element slot classification the node-based
// emittuplerow helpers expect; cstage drives the same off the tuple
// tinfo's params. Bounded to inline N_TTUPLE element types.
let tupnode: *node = nil;
if (sltnode != nil) { tupnode = sltnode.lhs; };
let istuprow: bool = false;
if (eu != nil && eu.kind == tykind.TY_TUPLE && tupnode != nil) {
if (tupnode.kind == nkind.N_TTUPLE) { istuprow = true; };
};
if (istuprow) {
let stride: i32 = etype.size: i32;
// Validate every row before any bytes (two-pass, partial-row
// safe).
let e2: *node = rhs.list;
for (e2 != nil) {
let row: *node = e2;
for (row != nil && row.kind == nkind.N_CAST) { row = row.lhs; };
let bad: bool = false;
if (row == nil) { bad = true; }
else if (row.kind != nkind.N_TUPLE) { bad = true; }
else if (!tuplerowfoldable(c, tupnode, row)) { bad = true; };
if (bad) {
let m: str = "emitslicedata: tuple-row element not a foldable constant ((str,*fn) rows only; #117, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
e2 = e2.next;
};
// Backing: k rows, bytes (one DATAW) then per-row relocs.
emitline("DATAW ");
emitsymnamehint(c, name, module);
emitline(".d(SB),\"");
e2 = rhs.list;
for (e2 != nil) {
let row: *node = e2;
for (row != nil && row.kind == nkind.N_CAST) { row = row.lhs; };
emittuplerowbytes(c, tupnode, row);
e2 = e2.next;
};
emitline("\"\n");
let rowoff: i32 = 0;
e2 = rhs.list;
for (e2 != nil) {
let row: *node = e2;
for (row != nil && row.kind == nkind.N_CAST) { row = row.lhs; };
emittuplerowrelocs(c, name, module, true, rowoff, tupnode, row);
rowoff += stride;
e2 = e2.next;
};
} else {
if (eu != nil) {
if (eu.kind == tykind.TY_STR || eu.kind == tykind.TY_SLICE
|| eu.kind == tykind.TY_TAGGED) {
let m: str = "emitslicedata: slice-of-{str,slice,tagged} literal static-init unsupported (#10 follow-up, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
let esz: i32 = etype.size: i32;
// Synthesize [k]T to ride the emitarraylitbytes choke-point.
let arrt: *tinfo = newtype(tykind.TY_ARRAY);
arrt.sub = etype;
arrt.alen = k: u64;
arrt.size = (k * esz): u64;
if (!emitarraylitbytes(c, arrt, rhs, 0)) {
let m: str = "emitslicedata: slice-literal element not a foldable constant (#10, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
// Writable backing data.
emitline("DATAW ");
emitsymnamehint(c, name, module);
emitline(".d(SB),\"");
emitarraylitbytes(c, arrt, rhs, 1);
emitline("\"\n");
};
// Writable backing data.
emitline("DATAW ");
emitsymnamehint(c, name, module);
emitline(".d(SB),\"");
emitarraylitbytes(c, arrt, rhs, 1);
emitline("\"\n");
// 24B header: ptr placeholder + LE len + LE cap (both = k). Word
// sizes from the type table (rule 13).
emitline("DATAW ");
@@ -39850,6 +39946,23 @@ fn emittaggeddata(c: *cgen, name: str, module: str, tt: *node, rhs: *node, sz: i
return true;
};
// nodefnptr — true if `ev` (casts already peeled by the caller) is the
// address-of a top-level fn (`&f`). The detect-half of the FIRST &fn→DATAR
// reloc machinery (#117 slice-row + #119 scalar-global); mirrors the
// address-of-fn codegen arm (fnretlookup at the N_UN TK_AMP ident,
// cgenexpr.ww). The reloc target symbol is emitted via emitfnname at the
// call site (cstage node_fnptr_sym returns the mangled string directly).
fn nodefnptr(c: *cgen, ev: *node) bool = {
if (ev == nil) { return false; };
if (ev.kind != nkind.N_UN) { return false; };
if (ev.op != tkind.TK_AMP) { return false; };
let opnd: *node = ev.lhs;
if (opnd == nil) { return false; };
if (opnd.kind != nkind.N_IDENT) { return false; };
if (fnretlookup(c, opnd.str) == nil) { return false; };
return true;
};
// tuplerowfoldable — validate every cast-peeled element of `rhs` (an
// N_TUPLE) reduces to a static row: an int literal (foldintliteral) or a
// str literal in a str/slice slot. A tagged element slot has no
@@ -39880,6 +39993,8 @@ fn tuplerowfoldable(c: *cgen, tt: *node, rhs: *node) bool = {
let wide: bool = isstrtype(c, et) || isslicetype(c, et);
if (wide) {
if (ev.kind != nkind.N_STRLIT) { return false; };
} else if (nodefnptr(c, ev)) {
// #117: a `&fn` element folds to an 8B reloc slot.
} else {
let v: u64 = 0u64;
if (!foldintliteral(ev, &v)) { return false; };
@@ -39918,6 +40033,11 @@ fn emittuplerowbytes(c: *cgen, tt: *node, rhs: *node) void = {
i = 16;
let ssz: i32 = primtypesize("str"): i32;
for (i < ssz) { emitdatawbyte(0u8); i += 1; };
} else if (nodefnptr(c, ev)) {
// #117: a `&fn` element is an 8B zero ptr placeholder;
// the reloc is patched in emittuplerowrelocs.
let i: i32 = 0;
for (i < 8) { emitdatawbyte(0u8); i += 1; };
} else {
let v: u64 = 0u64;
foldintliteral(ev, &v);
@@ -39965,6 +40085,19 @@ fn emittuplerowrelocs(c: *cgen, name: str, module: str, backing: bool, rowoff: i
};
foff += (tyslicesize(): i32);
} else {
// #117: the `&fn` element's reloc — the FIRST &fn→DATAR
// in the emitter; patches the 8B slot at holder+foff
// with the fn's TEXT VA via emitfnname.
if (nodefnptr(c, ev)) {
emitline("DATAR ");
emitsymnamehint(c, name, module);
if (backing) { emitline(".d"); };
emitline("+");
emitint(foff: i64);
emitline("(SB),");
emitfnname(c, ev.lhs.str, c.curmod);
emitline("(SB)\n");
};
// #22: slot stride via the accessor (tagged is
// rejected upstream; non-wide is 8 today — keeps the
// stride on the accessor scale).
@@ -40220,7 +40353,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
// only/`...` shapes (rule 7).
if (r != nil && r.kind == nkind.N_ARRLIT) {
emitslicedata(c, nm, d.nmod,
d.lhs.type_: *tinfo, r);
d.lhs.type_: *tinfo, d.lhs, r);
} else {
// zero-init: accept no rhs or nil.
// Any other rhs is skipped →

View File

@@ -1390,6 +1390,49 @@ export fn letpreintern(c: *cgen, file: *node) void = {
// #87: tagged global with a str/slice-variant literal init —
// pre-intern so emittaggeddata's DATAR (ptr@+8) finds its _S_
// rodata row (the #48 tuple-arm pattern; cstage letpreintern twin).
// #117: slice-of-tuple global — pre-intern each row's
// str-element literals in row-then-element order so
// emitslicedata's per-row DATAR patches find their _S_
// rodata rows (cstage letpreintern twin). Bounded to
// inline N_TTUPLE element types.
if (!handled && r != nil && d.lhs != nil) {
if (r.kind == nkind.N_ARRLIT
&& d.lhs.kind == nkind.N_TSLICE) {
let tupnode: *node = d.lhs.lhs;
if (tupnode != nil && tupnode.kind == nkind.N_TTUPLE) {
handled = true;
let row: *node = r.list;
for (row != nil) {
let rw: *node = row;
for (rw != nil && rw.kind == nkind.N_CAST) {
rw = rw.lhs;
};
if (rw != nil && rw.kind == nkind.N_TUPLE) {
let tp: *node = tupnode.list;
let e: *node = rw.list;
for (e != nil && tp != nil) {
let et: *node = tp.lhs;
let ev: *node = e;
for (ev != nil && ev.kind == nkind.N_CAST) {
ev = ev.lhs;
};
if (ev != nil) {
if (ev.kind == nkind.N_STRLIT
&& (isstrtype(c, et) || isslicetype(c, et))) {
if (ev.str.len > 0) {
internstrlit(c, ev.str);
};
};
};
e = e.next;
tp = tp.next;
};
};
row = row.next;
};
};
};
};
if (!handled && r != nil && d.lhs != nil) {
let tlt: *node = d.lhs;
for (tlt != nil && tlt.kind == nkind.N_TNAME) {
@@ -2130,7 +2173,7 @@ fn emitarraydata(c: *cgen, directive: str, name: str, module: str,
// and slice-of-{str,slice,tagged} elements (per-element relocs / #17)
// all loud-stop (rule 7, #10 follow-ups).
fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
rhs: *node) void = {
sltnode: *node, rhs: *node) void = {
let su: *tinfo = slt;
su = tichase(su);
// Defensive, mirrors cstage emit_slice_data's
@@ -2142,14 +2185,6 @@ fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
let etype: *tinfo = su.sub;
let eu: *tinfo = etype;
eu = tichase(eu);
if (eu != nil) {
if (eu.kind == tykind.TY_STR || eu.kind == tykind.TY_SLICE
|| eu.kind == tykind.TY_TAGGED) {
let m: str = "emitslicedata: slice-of-{str,slice,tagged} literal static-init unsupported (#10 follow-up, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
// Count elements; reject `...` (a slice literal has no target N).
let k: i32 = 0;
let e: *node = rhs.list;
@@ -2164,23 +2199,84 @@ fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
k += 1;
e = e.next;
};
let esz: i32 = etype.size: i32;
// Synthesize [k]T to ride the emitarraylitbytes choke-point.
let arrt: *tinfo = newtype(tykind.TY_ARRAY);
arrt.sub = etype;
arrt.alen = k: u64;
arrt.size = (k * esz): u64;
if (!emitarraylitbytes(c, arrt, rhs, 0)) {
let m: str = "emitslicedata: slice-literal element not a foldable constant (#10, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
// #117 aggregate-element arm: a slice of inline (str,*fn)-style
// TUPLE rows. The element type-AST node (sltnode.lhs = N_TTUPLE)
// drives the per-element slot classification the node-based
// emittuplerow helpers expect; cstage drives the same off the tuple
// tinfo's params. Bounded to inline N_TTUPLE element types.
let tupnode: *node = nil;
if (sltnode != nil) { tupnode = sltnode.lhs; };
let istuprow: bool = false;
if (eu != nil && eu.kind == tykind.TY_TUPLE && tupnode != nil) {
if (tupnode.kind == nkind.N_TTUPLE) { istuprow = true; };
};
if (istuprow) {
let stride: i32 = etype.size: i32;
// Validate every row before any bytes (two-pass, partial-row
// safe).
let e2: *node = rhs.list;
for (e2 != nil) {
let row: *node = e2;
for (row != nil && row.kind == nkind.N_CAST) { row = row.lhs; };
let bad: bool = false;
if (row == nil) { bad = true; }
else if (row.kind != nkind.N_TUPLE) { bad = true; }
else if (!tuplerowfoldable(c, tupnode, row)) { bad = true; };
if (bad) {
let m: str = "emitslicedata: tuple-row element not a foldable constant ((str,*fn) rows only; #117, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
e2 = e2.next;
};
// Backing: k rows, bytes (one DATAW) then per-row relocs.
emitline("DATAW ");
emitsymnamehint(c, name, module);
emitline(".d(SB),\"");
e2 = rhs.list;
for (e2 != nil) {
let row: *node = e2;
for (row != nil && row.kind == nkind.N_CAST) { row = row.lhs; };
emittuplerowbytes(c, tupnode, row);
e2 = e2.next;
};
emitline("\"\n");
let rowoff: i32 = 0;
e2 = rhs.list;
for (e2 != nil) {
let row: *node = e2;
for (row != nil && row.kind == nkind.N_CAST) { row = row.lhs; };
emittuplerowrelocs(c, name, module, true, rowoff, tupnode, row);
rowoff += stride;
e2 = e2.next;
};
} else {
if (eu != nil) {
if (eu.kind == tykind.TY_STR || eu.kind == tykind.TY_SLICE
|| eu.kind == tykind.TY_TAGGED) {
let m: str = "emitslicedata: slice-of-{str,slice,tagged} literal static-init unsupported (#10 follow-up, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
let esz: i32 = etype.size: i32;
// Synthesize [k]T to ride the emitarraylitbytes choke-point.
let arrt: *tinfo = newtype(tykind.TY_ARRAY);
arrt.sub = etype;
arrt.alen = k: u64;
arrt.size = (k * esz): u64;
if (!emitarraylitbytes(c, arrt, rhs, 0)) {
let m: str = "emitslicedata: slice-literal element not a foldable constant (#10, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
// Writable backing data.
emitline("DATAW ");
emitsymnamehint(c, name, module);
emitline(".d(SB),\"");
emitarraylitbytes(c, arrt, rhs, 1);
emitline("\"\n");
};
// Writable backing data.
emitline("DATAW ");
emitsymnamehint(c, name, module);
emitline(".d(SB),\"");
emitarraylitbytes(c, arrt, rhs, 1);
emitline("\"\n");
// 24B header: ptr placeholder + LE len + LE cap (both = k). Word
// sizes from the type table (rule 13).
emitline("DATAW ");
@@ -2296,6 +2392,23 @@ fn emittaggeddata(c: *cgen, name: str, module: str, tt: *node, rhs: *node, sz: i
return true;
};
// nodefnptr — true if `ev` (casts already peeled by the caller) is the
// address-of a top-level fn (`&f`). The detect-half of the FIRST &fn→DATAR
// reloc machinery (#117 slice-row + #119 scalar-global); mirrors the
// address-of-fn codegen arm (fnretlookup at the N_UN TK_AMP ident,
// cgenexpr.ww). The reloc target symbol is emitted via emitfnname at the
// call site (cstage node_fnptr_sym returns the mangled string directly).
fn nodefnptr(c: *cgen, ev: *node) bool = {
if (ev == nil) { return false; };
if (ev.kind != nkind.N_UN) { return false; };
if (ev.op != tkind.TK_AMP) { return false; };
let opnd: *node = ev.lhs;
if (opnd == nil) { return false; };
if (opnd.kind != nkind.N_IDENT) { return false; };
if (fnretlookup(c, opnd.str) == nil) { return false; };
return true;
};
// tuplerowfoldable — validate every cast-peeled element of `rhs` (an
// N_TUPLE) reduces to a static row: an int literal (foldintliteral) or a
// str literal in a str/slice slot. A tagged element slot has no
@@ -2326,6 +2439,8 @@ fn tuplerowfoldable(c: *cgen, tt: *node, rhs: *node) bool = {
let wide: bool = isstrtype(c, et) || isslicetype(c, et);
if (wide) {
if (ev.kind != nkind.N_STRLIT) { return false; };
} else if (nodefnptr(c, ev)) {
// #117: a `&fn` element folds to an 8B reloc slot.
} else {
let v: u64 = 0u64;
if (!foldintliteral(ev, &v)) { return false; };
@@ -2364,6 +2479,11 @@ fn emittuplerowbytes(c: *cgen, tt: *node, rhs: *node) void = {
i = 16;
let ssz: i32 = primtypesize("str"): i32;
for (i < ssz) { emitdatawbyte(0u8); i += 1; };
} else if (nodefnptr(c, ev)) {
// #117: a `&fn` element is an 8B zero ptr placeholder;
// the reloc is patched in emittuplerowrelocs.
let i: i32 = 0;
for (i < 8) { emitdatawbyte(0u8); i += 1; };
} else {
let v: u64 = 0u64;
foldintliteral(ev, &v);
@@ -2411,6 +2531,19 @@ fn emittuplerowrelocs(c: *cgen, name: str, module: str, backing: bool, rowoff: i
};
foff += (tyslicesize(): i32);
} else {
// #117: the `&fn` element's reloc — the FIRST &fn→DATAR
// in the emitter; patches the 8B slot at holder+foff
// with the fn's TEXT VA via emitfnname.
if (nodefnptr(c, ev)) {
emitline("DATAR ");
emitsymnamehint(c, name, module);
if (backing) { emitline(".d"); };
emitline("+");
emitint(foff: i64);
emitline("(SB),");
emitfnname(c, ev.lhs.str, c.curmod);
emitline("(SB)\n");
};
// #22: slot stride via the accessor (tagged is
// rejected upstream; non-wide is 8 today — keeps the
// stride on the accessor scale).
@@ -2666,7 +2799,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
// only/`...` shapes (rule 7).
if (r != nil && r.kind == nkind.N_ARRLIT) {
emitslicedata(c, nm, d.nmod,
d.lhs.type_: *tinfo, r);
d.lhs.type_: *tinfo, d.lhs, r);
} else {
// zero-init: accept no rhs or nil.
// Any other rhs is skipped →

View File

@@ -38944,6 +38944,49 @@ export fn letpreintern(c: *cgen, file: *node) void = {
// #87: tagged global with a str/slice-variant literal init —
// pre-intern so emittaggeddata's DATAR (ptr@+8) finds its _S_
// rodata row (the #48 tuple-arm pattern; cstage letpreintern twin).
// #117: slice-of-tuple global — pre-intern each row's
// str-element literals in row-then-element order so
// emitslicedata's per-row DATAR patches find their _S_
// rodata rows (cstage letpreintern twin). Bounded to
// inline N_TTUPLE element types.
if (!handled && r != nil && d.lhs != nil) {
if (r.kind == nkind.N_ARRLIT
&& d.lhs.kind == nkind.N_TSLICE) {
let tupnode: *node = d.lhs.lhs;
if (tupnode != nil && tupnode.kind == nkind.N_TTUPLE) {
handled = true;
let row: *node = r.list;
for (row != nil) {
let rw: *node = row;
for (rw != nil && rw.kind == nkind.N_CAST) {
rw = rw.lhs;
};
if (rw != nil && rw.kind == nkind.N_TUPLE) {
let tp: *node = tupnode.list;
let e: *node = rw.list;
for (e != nil && tp != nil) {
let et: *node = tp.lhs;
let ev: *node = e;
for (ev != nil && ev.kind == nkind.N_CAST) {
ev = ev.lhs;
};
if (ev != nil) {
if (ev.kind == nkind.N_STRLIT
&& (isstrtype(c, et) || isslicetype(c, et))) {
if (ev.str.len > 0) {
internstrlit(c, ev.str);
};
};
};
e = e.next;
tp = tp.next;
};
};
row = row.next;
};
};
};
};
if (!handled && r != nil && d.lhs != nil) {
let tlt: *node = d.lhs;
for (tlt != nil && tlt.kind == nkind.N_TNAME) {
@@ -39684,7 +39727,7 @@ fn emitarraydata(c: *cgen, directive: str, name: str, module: str,
// and slice-of-{str,slice,tagged} elements (per-element relocs / #17)
// all loud-stop (rule 7, #10 follow-ups).
fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
rhs: *node) void = {
sltnode: *node, rhs: *node) void = {
let su: *tinfo = slt;
su = tichase(su);
// Defensive, mirrors cstage emit_slice_data's
@@ -39696,14 +39739,6 @@ fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
let etype: *tinfo = su.sub;
let eu: *tinfo = etype;
eu = tichase(eu);
if (eu != nil) {
if (eu.kind == tykind.TY_STR || eu.kind == tykind.TY_SLICE
|| eu.kind == tykind.TY_TAGGED) {
let m: str = "emitslicedata: slice-of-{str,slice,tagged} literal static-init unsupported (#10 follow-up, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
// Count elements; reject `...` (a slice literal has no target N).
let k: i32 = 0;
let e: *node = rhs.list;
@@ -39718,23 +39753,84 @@ fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
k += 1;
e = e.next;
};
let esz: i32 = etype.size: i32;
// Synthesize [k]T to ride the emitarraylitbytes choke-point.
let arrt: *tinfo = newtype(tykind.TY_ARRAY);
arrt.sub = etype;
arrt.alen = k: u64;
arrt.size = (k * esz): u64;
if (!emitarraylitbytes(c, arrt, rhs, 0)) {
let m: str = "emitslicedata: slice-literal element not a foldable constant (#10, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
// #117 aggregate-element arm: a slice of inline (str,*fn)-style
// TUPLE rows. The element type-AST node (sltnode.lhs = N_TTUPLE)
// drives the per-element slot classification the node-based
// emittuplerow helpers expect; cstage drives the same off the tuple
// tinfo's params. Bounded to inline N_TTUPLE element types.
let tupnode: *node = nil;
if (sltnode != nil) { tupnode = sltnode.lhs; };
let istuprow: bool = false;
if (eu != nil && eu.kind == tykind.TY_TUPLE && tupnode != nil) {
if (tupnode.kind == nkind.N_TTUPLE) { istuprow = true; };
};
if (istuprow) {
let stride: i32 = etype.size: i32;
// Validate every row before any bytes (two-pass, partial-row
// safe).
let e2: *node = rhs.list;
for (e2 != nil) {
let row: *node = e2;
for (row != nil && row.kind == nkind.N_CAST) { row = row.lhs; };
let bad: bool = false;
if (row == nil) { bad = true; }
else if (row.kind != nkind.N_TUPLE) { bad = true; }
else if (!tuplerowfoldable(c, tupnode, row)) { bad = true; };
if (bad) {
let m: str = "emitslicedata: tuple-row element not a foldable constant ((str,*fn) rows only; #117, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
e2 = e2.next;
};
// Backing: k rows, bytes (one DATAW) then per-row relocs.
emitline("DATAW ");
emitsymnamehint(c, name, module);
emitline(".d(SB),\"");
e2 = rhs.list;
for (e2 != nil) {
let row: *node = e2;
for (row != nil && row.kind == nkind.N_CAST) { row = row.lhs; };
emittuplerowbytes(c, tupnode, row);
e2 = e2.next;
};
emitline("\"\n");
let rowoff: i32 = 0;
e2 = rhs.list;
for (e2 != nil) {
let row: *node = e2;
for (row != nil && row.kind == nkind.N_CAST) { row = row.lhs; };
emittuplerowrelocs(c, name, module, true, rowoff, tupnode, row);
rowoff += stride;
e2 = e2.next;
};
} else {
if (eu != nil) {
if (eu.kind == tykind.TY_STR || eu.kind == tykind.TY_SLICE
|| eu.kind == tykind.TY_TAGGED) {
let m: str = "emitslicedata: slice-of-{str,slice,tagged} literal static-init unsupported (#10 follow-up, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
};
let esz: i32 = etype.size: i32;
// Synthesize [k]T to ride the emitarraylitbytes choke-point.
let arrt: *tinfo = newtype(tykind.TY_ARRAY);
arrt.sub = etype;
arrt.alen = k: u64;
arrt.size = (k * esz): u64;
if (!emitarraylitbytes(c, arrt, rhs, 0)) {
let m: str = "emitslicedata: slice-literal element not a foldable constant (#10, rule 7)\n";
os.write(2, m.ptr, m.len: u64);
os.exit(1);
};
// Writable backing data.
emitline("DATAW ");
emitsymnamehint(c, name, module);
emitline(".d(SB),\"");
emitarraylitbytes(c, arrt, rhs, 1);
emitline("\"\n");
};
// Writable backing data.
emitline("DATAW ");
emitsymnamehint(c, name, module);
emitline(".d(SB),\"");
emitarraylitbytes(c, arrt, rhs, 1);
emitline("\"\n");
// 24B header: ptr placeholder + LE len + LE cap (both = k). Word
// sizes from the type table (rule 13).
emitline("DATAW ");
@@ -39850,6 +39946,23 @@ fn emittaggeddata(c: *cgen, name: str, module: str, tt: *node, rhs: *node, sz: i
return true;
};
// nodefnptr — true if `ev` (casts already peeled by the caller) is the
// address-of a top-level fn (`&f`). The detect-half of the FIRST &fn→DATAR
// reloc machinery (#117 slice-row + #119 scalar-global); mirrors the
// address-of-fn codegen arm (fnretlookup at the N_UN TK_AMP ident,
// cgenexpr.ww). The reloc target symbol is emitted via emitfnname at the
// call site (cstage node_fnptr_sym returns the mangled string directly).
fn nodefnptr(c: *cgen, ev: *node) bool = {
if (ev == nil) { return false; };
if (ev.kind != nkind.N_UN) { return false; };
if (ev.op != tkind.TK_AMP) { return false; };
let opnd: *node = ev.lhs;
if (opnd == nil) { return false; };
if (opnd.kind != nkind.N_IDENT) { return false; };
if (fnretlookup(c, opnd.str) == nil) { return false; };
return true;
};
// tuplerowfoldable — validate every cast-peeled element of `rhs` (an
// N_TUPLE) reduces to a static row: an int literal (foldintliteral) or a
// str literal in a str/slice slot. A tagged element slot has no
@@ -39880,6 +39993,8 @@ fn tuplerowfoldable(c: *cgen, tt: *node, rhs: *node) bool = {
let wide: bool = isstrtype(c, et) || isslicetype(c, et);
if (wide) {
if (ev.kind != nkind.N_STRLIT) { return false; };
} else if (nodefnptr(c, ev)) {
// #117: a `&fn` element folds to an 8B reloc slot.
} else {
let v: u64 = 0u64;
if (!foldintliteral(ev, &v)) { return false; };
@@ -39918,6 +40033,11 @@ fn emittuplerowbytes(c: *cgen, tt: *node, rhs: *node) void = {
i = 16;
let ssz: i32 = primtypesize("str"): i32;
for (i < ssz) { emitdatawbyte(0u8); i += 1; };
} else if (nodefnptr(c, ev)) {
// #117: a `&fn` element is an 8B zero ptr placeholder;
// the reloc is patched in emittuplerowrelocs.
let i: i32 = 0;
for (i < 8) { emitdatawbyte(0u8); i += 1; };
} else {
let v: u64 = 0u64;
foldintliteral(ev, &v);
@@ -39965,6 +40085,19 @@ fn emittuplerowrelocs(c: *cgen, name: str, module: str, backing: bool, rowoff: i
};
foff += (tyslicesize(): i32);
} else {
// #117: the `&fn` element's reloc — the FIRST &fn→DATAR
// in the emitter; patches the 8B slot at holder+foff
// with the fn's TEXT VA via emitfnname.
if (nodefnptr(c, ev)) {
emitline("DATAR ");
emitsymnamehint(c, name, module);
if (backing) { emitline(".d"); };
emitline("+");
emitint(foff: i64);
emitline("(SB),");
emitfnname(c, ev.lhs.str, c.curmod);
emitline("(SB)\n");
};
// #22: slot stride via the accessor (tagged is
// rejected upstream; non-wide is 8 today — keeps the
// stride on the accessor scale).
@@ -40220,7 +40353,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
// only/`...` shapes (rule 7).
if (r != nil && r.kind == nkind.N_ARRLIT) {
emitslicedata(c, nm, d.nmod,
d.lhs.type_: *tinfo, r);
d.lhs.type_: *tinfo, d.lhs, r);
} else {
// zero-init: accept no rhs or nil.
// Any other rhs is skipped →