wcc/cgen: #87 plain tagged-union module-global DATA + match SB-resolution (both-stage)
A PLAIN (non-alias) module-level tagged-union global SEGV'd on BOTH
stages: no static DATA was emitted (let_emit_size/letemitsize returned 0
for TY_TAGGED) so the global was never registered, and the match
scrutinee resolved it as a frame-local at offset 0 — reading saved BP as
the tag. Two sub-sites, one route (neither half ships alone — DATA
without SB-resolution still SEGVs; SB-resolution without DATA reads
nothing):
(a) DATA-emitter — a non-nullable TY_TAGGED arm emits the box that
byte-MIRRORS a runtime LOCAL of the same type: tag word at +0 (the
const-selected variant index via cg_tag_for_variant / taggedvariant-
index), payload at +8, zero-padded to the union box size. int and
str/slice literal variants are wired (str carries a DATAR ptr patch
at +8); any other variant payload loud-stops (rule 7). emit_tagged_
data + emittaggeddata are the per-stage twins; let_pre_intern/
letpreintern gain the matching str-variant intern. Nullable stays 0
so the (*T|void) one-word fold keeps the 8B scalar arm.
(b) match-scrutinee global resolution — the PLAIN-tagged twin of #78:
a global tagged ident scrutinee LEAQs name(SB) and copies the box
into an @match_spill slot the dispatch indexes off BP.
DATA target (mirror of the local box, verified byte-for-byte): for
(i32|str)=42 the 32B box is tag0 | 42@8 | zero-pad; for ="x" it is
tag1 | ptr0@8(DATAR _S_n) | len@16 | cap@24. cs and ww emit byte-
identical asm.
Pins (rob §3, dual-stage 910 cstage + 997 wwstage, attest_pass.ww): the
tagged global-vs-local byte-identity pin (match over the GLOBAL gives the
same arm/value as over a LOCAL — was SEGV both stages) and the str-
variant tag-1 pin, plus the #86 tuple global-vs-local lock-pin guarding
the already-correct emitter path.
929 fail_global_src graduates: a >48B tagged GLOBAL by-value arg now
resolves through the cgplaceaddr MEMORY-class arm (LEAQ g(SB) + blit)
instead of the #38b loud-stop, and runs correctly (uninit zero box ->
first variant); the row becomes a positive run pin. The struct-variant
>48B init still loud-stops via the data emitter.
The first-class-VALUE copy of a tagged ident (`let q = g`) stays a
pre-existing silent #49/#46 sibling (local and global identically),
filed separately — out of this fold's two sub-sites.
This commit is contained in:
@@ -23730,7 +23730,33 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
if (lc != nil) {
|
||||
scrutoff = lc.off;
|
||||
scrutt = resolvetagged(c, lc.tnode);
|
||||
};
|
||||
} else { if (isletvar(c, scrut.str)) {
|
||||
// #87: a tagged-union GLOBAL scrutinee — the box lives
|
||||
// in static DATA at name(SB), not the BP frame.
|
||||
// localfindnode returns nil and the dispatch would read
|
||||
// saved BP as the tag (garbage). The PLAIN-tagged twin
|
||||
// of cstage #78 SB-resolution: LEAQ the address, copy
|
||||
// the box into an @match_spill slot indexed off BP.
|
||||
let gtt: *node = resolvetagged(c, letvartnode(c, scrut.str));
|
||||
if (gtt != nil && !isnullabletype(gtt)) {
|
||||
scrutt = gtt;
|
||||
let gspill: i32 = matchspillsz(c, gtt);
|
||||
scrutoff = localalloc(c, "@match_spill", gspill, nil);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymnamehint(c, scrut.str, c.curmod);
|
||||
emitline("(SB), AX\n");
|
||||
let gk: i32 = 0;
|
||||
for (gk < gspill) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(gk: i64, "AX");
|
||||
emitline(", DX\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((scrutoff + gk): i64);
|
||||
emitline("(BP)\n");
|
||||
gk += 8;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
} else {
|
||||
// Non-ident scrutinee (call result, arr[i], p.field,
|
||||
// ?, etc.). Spill into an `@match_spill` scratch slot
|
||||
@@ -38229,6 +38255,14 @@ fn letemitsize(c: *cgen, d: *node) i32 = {
|
||||
};
|
||||
return tsum;
|
||||
};
|
||||
// #87: non-nullable tagged-union global — box size (tag word +
|
||||
// max payload, mirror of the runtime local). Nullable stays 0 so
|
||||
// the (*T|void) one-word fold keeps the 8B scalar arm in
|
||||
// emitletdataw. Mirrors cstage let_emit_size TY_TAGGED.
|
||||
if (t.kind == nkind.N_TTAGGED) {
|
||||
if (isnullabletype(t)) { return 0; };
|
||||
return slotsize(c, t);
|
||||
};
|
||||
if (t.kind != nkind.N_TNAME) { return 0; };
|
||||
let nm: str = t.str;
|
||||
if (letscalarprim(nm)) { return 8; };
|
||||
@@ -38591,6 +38625,24 @@ 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).
|
||||
if (!handled && r != nil && d.lhs != nil) {
|
||||
let tlt: *node = d.lhs;
|
||||
for (tlt != nil && tlt.kind == nkind.N_TNAME) {
|
||||
tlt = aliaslookup(c, tlt.str);
|
||||
};
|
||||
if (tlt != nil) {
|
||||
if (tlt.kind == nkind.N_TTAGGED && !isnullabletype(tlt)) {
|
||||
if (r.kind == nkind.N_STRLIT && r.str.len > 0
|
||||
&& (nodeisstr(c, r) || nodeisslice(c, r))) {
|
||||
handled = true;
|
||||
internstrlit(c, r.str);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (!handled) {
|
||||
let sz: i32 = letemitsize(c, d);
|
||||
// #43: route the str-let gate through primtypesize so
|
||||
@@ -39399,6 +39451,89 @@ fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
|
||||
emitline(".d(SB)\n");
|
||||
};
|
||||
|
||||
// emittaggeddata — module-level `let g: (T0 | T1 | ...) = v;` static
|
||||
// init (#87). Byte-MIRRORS a runtime LOCAL tagged box (rob §3 SSoT pin):
|
||||
// tag word at +0 (the const-selected variant index, taggedvariantindex —
|
||||
// the routine the runtime widen + match dispatch key on), payload at +8,
|
||||
// zero-padded to the union box size `sz`. int and str-literal variants
|
||||
// are wired (the Hare-stdlib shapes, ref/hare/time/chrono/utc.ha:44); any
|
||||
// other variant payload returns false and the caller loud-stops (rule 7) —
|
||||
// never the pre-#87 silent no-DATA + garbage read. Mirror of cstage
|
||||
// emit_tagged_data.
|
||||
fn emittaggeddata(c: *cgen, name: str, module: str, tt: *node, rhs: *node, sz: i32) bool = {
|
||||
if (tt == nil) { return false; };
|
||||
if (rhs == nil) {
|
||||
emitline("DATAW ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("(SB),\"");
|
||||
let zi: i32 = 0;
|
||||
for (zi < sz) { emitdatawbyte(0u8); zi += 1; };
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
let r: *node = rhs;
|
||||
for (r != nil && r.kind == nkind.N_CAST) { r = r.lhs; };
|
||||
if (r == nil) { return false; };
|
||||
let tag: i32 = taggedvariantindex(c, tt, r);
|
||||
if (tag < 0) { return false; };
|
||||
let wide: bool = nodeisstr(c, r) || nodeisslice(c, r);
|
||||
let i: i32 = 0;
|
||||
let acc: u64 = 0u64;
|
||||
if (wide) {
|
||||
if (r.kind != nkind.N_STRLIT) { return false; };
|
||||
let lv: u64 = r.str.len: u64;
|
||||
emitline("DATAW ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("(SB),\"");
|
||||
// tag@0
|
||||
acc = tag: u64;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// ptr placeholder@8
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte(0u8); i += 1; };
|
||||
// len@16
|
||||
acc = lv;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// cap@24 (= len for a static str literal, mirroring the box)
|
||||
acc = lv;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// pad to sz
|
||||
i = 32;
|
||||
for (i < sz) { emitdatawbyte(0u8); i += 1; };
|
||||
emitline("\"\n");
|
||||
if (r.str.len > 0) {
|
||||
let lab: str = internstrlit(c, r.str);
|
||||
emitline("DATAR ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("+8(SB),");
|
||||
emitbytes( lab.ptr, lab.len: u64);
|
||||
emitline("(SB)\n");
|
||||
};
|
||||
return true;
|
||||
};
|
||||
let v: u64 = 0u64;
|
||||
if (!foldintliteral(r, &v)) { return false; };
|
||||
emitline("DATAW ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("(SB),\"");
|
||||
// tag@0
|
||||
acc = tag: u64;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// payload@8
|
||||
acc = v;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// pad to sz
|
||||
i = 16;
|
||||
for (i < sz) { emitdatawbyte(0u8); i += 1; };
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
|
||||
// emittupledata — module-level `let g: (T0, T1, ...) = (v0, ...);`
|
||||
// static init (C-t3, #48). Slot layout (C-t0): a scalar element is one
|
||||
// 8B LE word, a str element its 24B header slot (8 zero ptr placeholder
|
||||
@@ -39582,6 +39717,24 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
// #87: non-nullable tagged-union global — emit the box
|
||||
// mirroring the runtime local (tag + payload). letemitsize
|
||||
// keeps nullable at 0 so the (*T|void) one-word fold stays
|
||||
// on the 8B scalar arm below. The istagged gate also keeps
|
||||
// a tagged box (size can equal str/slice size) out of those.
|
||||
let istagged: bool = false;
|
||||
if (tlt != nil) {
|
||||
if (tlt.kind == nkind.N_TTAGGED) {
|
||||
if (!isnullabletype(tlt)) { istagged = true; };
|
||||
};
|
||||
};
|
||||
if (istagged) {
|
||||
if (!emittaggeddata(c, nm, d.nmod, tlt, d.rhs, sz)) {
|
||||
let mtg: str = "global tagged let: unsupported variant init (int/str literal only; rule 7)\n";
|
||||
os.write(2, mtg.ptr, mtg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
if (fsz > 0) {
|
||||
// Float global: routes through the
|
||||
// emitfloatlitdata SSoT helper, shared
|
||||
@@ -39616,7 +39769,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
if (dti != nil) {
|
||||
if (dti.kind == tykind.TY_ARRAY) { isarr8 = true; };
|
||||
};
|
||||
if (sz == 8 && !issg && fsz == 0 && !isarr8 && !istup) {
|
||||
if (sz == 8 && !issg && fsz == 0 && !isarr8 && !istup && !istagged) {
|
||||
let v: u64 = 0u64;
|
||||
let ok: bool = true;
|
||||
if (d.rhs != nil) {
|
||||
@@ -39647,7 +39800,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitline("\"\n");
|
||||
};
|
||||
};
|
||||
if (sz == primtypesize("str"): i32 && !issg && !istup && !letvarisslice(c, nm)) {
|
||||
if (sz == primtypesize("str"): i32 && !issg && !istup && !istagged && !letvarisslice(c, nm)) {
|
||||
let r: *node = d.rhs;
|
||||
for (r != nil) {
|
||||
if (r.kind != nkind.N_CAST) { break; };
|
||||
@@ -39712,7 +39865,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
if (sz == tyslicesize(): i32 && !issg && letvarisslice(c, nm)) {
|
||||
if (sz == tyslicesize(): i32 && !issg && !istagged && letvarisslice(c, nm)) {
|
||||
let r: *node = d.rhs;
|
||||
for (r != nil) {
|
||||
if (r.kind != nkind.N_CAST) { break; };
|
||||
|
||||
@@ -1017,6 +1017,14 @@ fn letemitsize(c: *cgen, d: *node) i32 = {
|
||||
};
|
||||
return tsum;
|
||||
};
|
||||
// #87: non-nullable tagged-union global — box size (tag word +
|
||||
// max payload, mirror of the runtime local). Nullable stays 0 so
|
||||
// the (*T|void) one-word fold keeps the 8B scalar arm in
|
||||
// emitletdataw. Mirrors cstage let_emit_size TY_TAGGED.
|
||||
if (t.kind == nkind.N_TTAGGED) {
|
||||
if (isnullabletype(t)) { return 0; };
|
||||
return slotsize(c, t);
|
||||
};
|
||||
if (t.kind != nkind.N_TNAME) { return 0; };
|
||||
let nm: str = t.str;
|
||||
if (letscalarprim(nm)) { return 8; };
|
||||
@@ -1379,6 +1387,24 @@ 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).
|
||||
if (!handled && r != nil && d.lhs != nil) {
|
||||
let tlt: *node = d.lhs;
|
||||
for (tlt != nil && tlt.kind == nkind.N_TNAME) {
|
||||
tlt = aliaslookup(c, tlt.str);
|
||||
};
|
||||
if (tlt != nil) {
|
||||
if (tlt.kind == nkind.N_TTAGGED && !isnullabletype(tlt)) {
|
||||
if (r.kind == nkind.N_STRLIT && r.str.len > 0
|
||||
&& (nodeisstr(c, r) || nodeisslice(c, r))) {
|
||||
handled = true;
|
||||
internstrlit(c, r.str);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (!handled) {
|
||||
let sz: i32 = letemitsize(c, d);
|
||||
// #43: route the str-let gate through primtypesize so
|
||||
@@ -2187,6 +2213,89 @@ fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
|
||||
emitline(".d(SB)\n");
|
||||
};
|
||||
|
||||
// emittaggeddata — module-level `let g: (T0 | T1 | ...) = v;` static
|
||||
// init (#87). Byte-MIRRORS a runtime LOCAL tagged box (rob §3 SSoT pin):
|
||||
// tag word at +0 (the const-selected variant index, taggedvariantindex —
|
||||
// the routine the runtime widen + match dispatch key on), payload at +8,
|
||||
// zero-padded to the union box size `sz`. int and str-literal variants
|
||||
// are wired (the Hare-stdlib shapes, ref/hare/time/chrono/utc.ha:44); any
|
||||
// other variant payload returns false and the caller loud-stops (rule 7) —
|
||||
// never the pre-#87 silent no-DATA + garbage read. Mirror of cstage
|
||||
// emit_tagged_data.
|
||||
fn emittaggeddata(c: *cgen, name: str, module: str, tt: *node, rhs: *node, sz: i32) bool = {
|
||||
if (tt == nil) { return false; };
|
||||
if (rhs == nil) {
|
||||
emitline("DATAW ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("(SB),\"");
|
||||
let zi: i32 = 0;
|
||||
for (zi < sz) { emitdatawbyte(0u8); zi += 1; };
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
let r: *node = rhs;
|
||||
for (r != nil && r.kind == nkind.N_CAST) { r = r.lhs; };
|
||||
if (r == nil) { return false; };
|
||||
let tag: i32 = taggedvariantindex(c, tt, r);
|
||||
if (tag < 0) { return false; };
|
||||
let wide: bool = nodeisstr(c, r) || nodeisslice(c, r);
|
||||
let i: i32 = 0;
|
||||
let acc: u64 = 0u64;
|
||||
if (wide) {
|
||||
if (r.kind != nkind.N_STRLIT) { return false; };
|
||||
let lv: u64 = r.str.len: u64;
|
||||
emitline("DATAW ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("(SB),\"");
|
||||
// tag@0
|
||||
acc = tag: u64;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// ptr placeholder@8
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte(0u8); i += 1; };
|
||||
// len@16
|
||||
acc = lv;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// cap@24 (= len for a static str literal, mirroring the box)
|
||||
acc = lv;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// pad to sz
|
||||
i = 32;
|
||||
for (i < sz) { emitdatawbyte(0u8); i += 1; };
|
||||
emitline("\"\n");
|
||||
if (r.str.len > 0) {
|
||||
let lab: str = internstrlit(c, r.str);
|
||||
emitline("DATAR ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("+8(SB),");
|
||||
emitbytes( lab.ptr, lab.len: u64);
|
||||
emitline("(SB)\n");
|
||||
};
|
||||
return true;
|
||||
};
|
||||
let v: u64 = 0u64;
|
||||
if (!foldintliteral(r, &v)) { return false; };
|
||||
emitline("DATAW ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("(SB),\"");
|
||||
// tag@0
|
||||
acc = tag: u64;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// payload@8
|
||||
acc = v;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// pad to sz
|
||||
i = 16;
|
||||
for (i < sz) { emitdatawbyte(0u8); i += 1; };
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
|
||||
// emittupledata — module-level `let g: (T0, T1, ...) = (v0, ...);`
|
||||
// static init (C-t3, #48). Slot layout (C-t0): a scalar element is one
|
||||
// 8B LE word, a str element its 24B header slot (8 zero ptr placeholder
|
||||
@@ -2370,6 +2479,24 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
// #87: non-nullable tagged-union global — emit the box
|
||||
// mirroring the runtime local (tag + payload). letemitsize
|
||||
// keeps nullable at 0 so the (*T|void) one-word fold stays
|
||||
// on the 8B scalar arm below. The istagged gate also keeps
|
||||
// a tagged box (size can equal str/slice size) out of those.
|
||||
let istagged: bool = false;
|
||||
if (tlt != nil) {
|
||||
if (tlt.kind == nkind.N_TTAGGED) {
|
||||
if (!isnullabletype(tlt)) { istagged = true; };
|
||||
};
|
||||
};
|
||||
if (istagged) {
|
||||
if (!emittaggeddata(c, nm, d.nmod, tlt, d.rhs, sz)) {
|
||||
let mtg: str = "global tagged let: unsupported variant init (int/str literal only; rule 7)\n";
|
||||
os.write(2, mtg.ptr, mtg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
if (fsz > 0) {
|
||||
// Float global: routes through the
|
||||
// emitfloatlitdata SSoT helper, shared
|
||||
@@ -2404,7 +2531,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
if (dti != nil) {
|
||||
if (dti.kind == tykind.TY_ARRAY) { isarr8 = true; };
|
||||
};
|
||||
if (sz == 8 && !issg && fsz == 0 && !isarr8 && !istup) {
|
||||
if (sz == 8 && !issg && fsz == 0 && !isarr8 && !istup && !istagged) {
|
||||
let v: u64 = 0u64;
|
||||
let ok: bool = true;
|
||||
if (d.rhs != nil) {
|
||||
@@ -2435,7 +2562,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitline("\"\n");
|
||||
};
|
||||
};
|
||||
if (sz == primtypesize("str"): i32 && !issg && !istup && !letvarisslice(c, nm)) {
|
||||
if (sz == primtypesize("str"): i32 && !issg && !istup && !istagged && !letvarisslice(c, nm)) {
|
||||
let r: *node = d.rhs;
|
||||
for (r != nil) {
|
||||
if (r.kind != nkind.N_CAST) { break; };
|
||||
@@ -2500,7 +2627,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
if (sz == tyslicesize(): i32 && !issg && letvarisslice(c, nm)) {
|
||||
if (sz == tyslicesize(): i32 && !issg && !istagged && letvarisslice(c, nm)) {
|
||||
let r: *node = d.rhs;
|
||||
for (r != nil) {
|
||||
if (r.kind != nkind.N_CAST) { break; };
|
||||
|
||||
@@ -2554,7 +2554,33 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
if (lc != nil) {
|
||||
scrutoff = lc.off;
|
||||
scrutt = resolvetagged(c, lc.tnode);
|
||||
};
|
||||
} else { if (isletvar(c, scrut.str)) {
|
||||
// #87: a tagged-union GLOBAL scrutinee — the box lives
|
||||
// in static DATA at name(SB), not the BP frame.
|
||||
// localfindnode returns nil and the dispatch would read
|
||||
// saved BP as the tag (garbage). The PLAIN-tagged twin
|
||||
// of cstage #78 SB-resolution: LEAQ the address, copy
|
||||
// the box into an @match_spill slot indexed off BP.
|
||||
let gtt: *node = resolvetagged(c, letvartnode(c, scrut.str));
|
||||
if (gtt != nil && !isnullabletype(gtt)) {
|
||||
scrutt = gtt;
|
||||
let gspill: i32 = matchspillsz(c, gtt);
|
||||
scrutoff = localalloc(c, "@match_spill", gspill, nil);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymnamehint(c, scrut.str, c.curmod);
|
||||
emitline("(SB), AX\n");
|
||||
let gk: i32 = 0;
|
||||
for (gk < gspill) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(gk: i64, "AX");
|
||||
emitline(", DX\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((scrutoff + gk): i64);
|
||||
emitline("(BP)\n");
|
||||
gk += 8;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
} else {
|
||||
// Non-ident scrutinee (call result, arr[i], p.field,
|
||||
// ?, etc.). Spill into an `@match_spill` scratch slot
|
||||
|
||||
@@ -23730,7 +23730,33 @@ fn cgmatch(c: *cgen, n: *node) void = {
|
||||
if (lc != nil) {
|
||||
scrutoff = lc.off;
|
||||
scrutt = resolvetagged(c, lc.tnode);
|
||||
};
|
||||
} else { if (isletvar(c, scrut.str)) {
|
||||
// #87: a tagged-union GLOBAL scrutinee — the box lives
|
||||
// in static DATA at name(SB), not the BP frame.
|
||||
// localfindnode returns nil and the dispatch would read
|
||||
// saved BP as the tag (garbage). The PLAIN-tagged twin
|
||||
// of cstage #78 SB-resolution: LEAQ the address, copy
|
||||
// the box into an @match_spill slot indexed off BP.
|
||||
let gtt: *node = resolvetagged(c, letvartnode(c, scrut.str));
|
||||
if (gtt != nil && !isnullabletype(gtt)) {
|
||||
scrutt = gtt;
|
||||
let gspill: i32 = matchspillsz(c, gtt);
|
||||
scrutoff = localalloc(c, "@match_spill", gspill, nil);
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymnamehint(c, scrut.str, c.curmod);
|
||||
emitline("(SB), AX\n");
|
||||
let gk: i32 = 0;
|
||||
for (gk < gspill) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(gk: i64, "AX");
|
||||
emitline(", DX\n");
|
||||
emitline("\tMOVQ\tDX, ");
|
||||
emitoff((scrutoff + gk): i64);
|
||||
emitline("(BP)\n");
|
||||
gk += 8;
|
||||
};
|
||||
};
|
||||
}; };
|
||||
} else {
|
||||
// Non-ident scrutinee (call result, arr[i], p.field,
|
||||
// ?, etc.). Spill into an `@match_spill` scratch slot
|
||||
@@ -38229,6 +38255,14 @@ fn letemitsize(c: *cgen, d: *node) i32 = {
|
||||
};
|
||||
return tsum;
|
||||
};
|
||||
// #87: non-nullable tagged-union global — box size (tag word +
|
||||
// max payload, mirror of the runtime local). Nullable stays 0 so
|
||||
// the (*T|void) one-word fold keeps the 8B scalar arm in
|
||||
// emitletdataw. Mirrors cstage let_emit_size TY_TAGGED.
|
||||
if (t.kind == nkind.N_TTAGGED) {
|
||||
if (isnullabletype(t)) { return 0; };
|
||||
return slotsize(c, t);
|
||||
};
|
||||
if (t.kind != nkind.N_TNAME) { return 0; };
|
||||
let nm: str = t.str;
|
||||
if (letscalarprim(nm)) { return 8; };
|
||||
@@ -38591,6 +38625,24 @@ 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).
|
||||
if (!handled && r != nil && d.lhs != nil) {
|
||||
let tlt: *node = d.lhs;
|
||||
for (tlt != nil && tlt.kind == nkind.N_TNAME) {
|
||||
tlt = aliaslookup(c, tlt.str);
|
||||
};
|
||||
if (tlt != nil) {
|
||||
if (tlt.kind == nkind.N_TTAGGED && !isnullabletype(tlt)) {
|
||||
if (r.kind == nkind.N_STRLIT && r.str.len > 0
|
||||
&& (nodeisstr(c, r) || nodeisslice(c, r))) {
|
||||
handled = true;
|
||||
internstrlit(c, r.str);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (!handled) {
|
||||
let sz: i32 = letemitsize(c, d);
|
||||
// #43: route the str-let gate through primtypesize so
|
||||
@@ -39399,6 +39451,89 @@ fn emitslicedata(c: *cgen, name: str, module: str, slt: *tinfo,
|
||||
emitline(".d(SB)\n");
|
||||
};
|
||||
|
||||
// emittaggeddata — module-level `let g: (T0 | T1 | ...) = v;` static
|
||||
// init (#87). Byte-MIRRORS a runtime LOCAL tagged box (rob §3 SSoT pin):
|
||||
// tag word at +0 (the const-selected variant index, taggedvariantindex —
|
||||
// the routine the runtime widen + match dispatch key on), payload at +8,
|
||||
// zero-padded to the union box size `sz`. int and str-literal variants
|
||||
// are wired (the Hare-stdlib shapes, ref/hare/time/chrono/utc.ha:44); any
|
||||
// other variant payload returns false and the caller loud-stops (rule 7) —
|
||||
// never the pre-#87 silent no-DATA + garbage read. Mirror of cstage
|
||||
// emit_tagged_data.
|
||||
fn emittaggeddata(c: *cgen, name: str, module: str, tt: *node, rhs: *node, sz: i32) bool = {
|
||||
if (tt == nil) { return false; };
|
||||
if (rhs == nil) {
|
||||
emitline("DATAW ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("(SB),\"");
|
||||
let zi: i32 = 0;
|
||||
for (zi < sz) { emitdatawbyte(0u8); zi += 1; };
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
let r: *node = rhs;
|
||||
for (r != nil && r.kind == nkind.N_CAST) { r = r.lhs; };
|
||||
if (r == nil) { return false; };
|
||||
let tag: i32 = taggedvariantindex(c, tt, r);
|
||||
if (tag < 0) { return false; };
|
||||
let wide: bool = nodeisstr(c, r) || nodeisslice(c, r);
|
||||
let i: i32 = 0;
|
||||
let acc: u64 = 0u64;
|
||||
if (wide) {
|
||||
if (r.kind != nkind.N_STRLIT) { return false; };
|
||||
let lv: u64 = r.str.len: u64;
|
||||
emitline("DATAW ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("(SB),\"");
|
||||
// tag@0
|
||||
acc = tag: u64;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// ptr placeholder@8
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte(0u8); i += 1; };
|
||||
// len@16
|
||||
acc = lv;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// cap@24 (= len for a static str literal, mirroring the box)
|
||||
acc = lv;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// pad to sz
|
||||
i = 32;
|
||||
for (i < sz) { emitdatawbyte(0u8); i += 1; };
|
||||
emitline("\"\n");
|
||||
if (r.str.len > 0) {
|
||||
let lab: str = internstrlit(c, r.str);
|
||||
emitline("DATAR ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("+8(SB),");
|
||||
emitbytes( lab.ptr, lab.len: u64);
|
||||
emitline("(SB)\n");
|
||||
};
|
||||
return true;
|
||||
};
|
||||
let v: u64 = 0u64;
|
||||
if (!foldintliteral(r, &v)) { return false; };
|
||||
emitline("DATAW ");
|
||||
emitsymnamehint(c, name, module);
|
||||
emitline("(SB),\"");
|
||||
// tag@0
|
||||
acc = tag: u64;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// payload@8
|
||||
acc = v;
|
||||
i = 0;
|
||||
for (i < 8) { emitdatawbyte((acc & 255u64): u8); acc = acc >> 8u64; i += 1; };
|
||||
// pad to sz
|
||||
i = 16;
|
||||
for (i < sz) { emitdatawbyte(0u8); i += 1; };
|
||||
emitline("\"\n");
|
||||
return true;
|
||||
};
|
||||
|
||||
// emittupledata — module-level `let g: (T0, T1, ...) = (v0, ...);`
|
||||
// static init (C-t3, #48). Slot layout (C-t0): a scalar element is one
|
||||
// 8B LE word, a str element its 24B header slot (8 zero ptr placeholder
|
||||
@@ -39582,6 +39717,24 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
// #87: non-nullable tagged-union global — emit the box
|
||||
// mirroring the runtime local (tag + payload). letemitsize
|
||||
// keeps nullable at 0 so the (*T|void) one-word fold stays
|
||||
// on the 8B scalar arm below. The istagged gate also keeps
|
||||
// a tagged box (size can equal str/slice size) out of those.
|
||||
let istagged: bool = false;
|
||||
if (tlt != nil) {
|
||||
if (tlt.kind == nkind.N_TTAGGED) {
|
||||
if (!isnullabletype(tlt)) { istagged = true; };
|
||||
};
|
||||
};
|
||||
if (istagged) {
|
||||
if (!emittaggeddata(c, nm, d.nmod, tlt, d.rhs, sz)) {
|
||||
let mtg: str = "global tagged let: unsupported variant init (int/str literal only; rule 7)\n";
|
||||
os.write(2, mtg.ptr, mtg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
if (fsz > 0) {
|
||||
// Float global: routes through the
|
||||
// emitfloatlitdata SSoT helper, shared
|
||||
@@ -39616,7 +39769,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
if (dti != nil) {
|
||||
if (dti.kind == tykind.TY_ARRAY) { isarr8 = true; };
|
||||
};
|
||||
if (sz == 8 && !issg && fsz == 0 && !isarr8 && !istup) {
|
||||
if (sz == 8 && !issg && fsz == 0 && !isarr8 && !istup && !istagged) {
|
||||
let v: u64 = 0u64;
|
||||
let ok: bool = true;
|
||||
if (d.rhs != nil) {
|
||||
@@ -39647,7 +39800,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitline("\"\n");
|
||||
};
|
||||
};
|
||||
if (sz == primtypesize("str"): i32 && !issg && !istup && !letvarisslice(c, nm)) {
|
||||
if (sz == primtypesize("str"): i32 && !issg && !istup && !istagged && !letvarisslice(c, nm)) {
|
||||
let r: *node = d.rhs;
|
||||
for (r != nil) {
|
||||
if (r.kind != nkind.N_CAST) { break; };
|
||||
@@ -39712,7 +39865,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
if (sz == tyslicesize(): i32 && !issg && letvarisslice(c, nm)) {
|
||||
if (sz == tyslicesize(): i32 && !issg && !istagged && letvarisslice(c, nm)) {
|
||||
let r: *node = d.rhs;
|
||||
for (r != nil) {
|
||||
if (r.kind != nkind.N_CAST) { break; };
|
||||
|
||||
Reference in New Issue
Block a user