diff --git a/lib/ww/typ.ww b/lib/ww/typ.ww index a0aa6ee5..ce0033d7 100644 --- a/lib/ww/typ.ww +++ b/lib/ww/typ.ww @@ -71,6 +71,15 @@ type tfield = struct { type tparam = struct { name: str, type_: *tinfo, + // #61a: per-variant `!T` error mark for TY_TAGGED variants. + // cstage-MIRROR divergence: harec carries no per-variant flag — + // it models `!T` as a distinct STORAGE_ERROR type node + // (ref/harec/include/types.h:144, src/types.c:151-159 + // type_is_error). wwstage tinfo has no iserror field + // (check.ww TTAGGED arm), so the bit rides the shared param + // struct instead, matching cstage Type.iserror semantics. + // Faithful STORAGE_ERROR-node port filed as #62. + iserror: bool, tnext: *tparam, }; diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 97226fcc..c8b9426e 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -6401,6 +6401,15 @@ type tfield = struct { type tparam = struct { name: str, type_: *tinfo, + // #61a: per-variant `!T` error mark for TY_TAGGED variants. + // cstage-MIRROR divergence: harec carries no per-variant flag — + // it models `!T` as a distinct STORAGE_ERROR type node + // (ref/harec/include/types.h:144, src/types.c:151-159 + // type_is_error). wwstage tinfo has no iserror field + // (check.ww TTAGGED arm), so the bit rides the shared param + // struct instead, matching cstage Type.iserror semantics. + // Faithful STORAGE_ERROR-node port filed as #62. + iserror: bool, tnext: *tparam, }; @@ -8427,12 +8436,19 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = { r = newtype(tykind.TY_TAGGED); tinfocachebind(c.tc, n, r); // #50 / A.6.3f phase 1: populate ti.params as a tparam linked - // list (head=first source variant) in lock-step with the - // size/align accumulator. Mirrors cstage check.c:374-396 — - // same Tparam shape ww reuses across struct-fields / tuple- - // fields / fn-params / tagged-variants (sea-of-stars per rule - // 12). Phase 2 (#50b) retires cgenutil's AST-keyed - // nullableptrtag onto the chain. + // list (head=first source variant). #61a: flatten `...inner` + // tagged spreads into the chain and stamp each variant's + // iserror. Mirrors cstage check.c:366-389 — dealias one NAMED + // level, require TY_TAGGED, splice its (already-flattened) + // variants in declaration order; otherwise append the single + // variant. size/align stays accounted off the surface member + // (vt), so r.size is byte-identical to pre-#61a: the flatten + + // iserror are additive, with no #61a-stage readers (the variant + // machinery + cgwidentaggedstore/matchscrutt migrate onto the + // chain in #61b/c). Same shared Tparam shape ww reuses across + // struct-fields / tuple-fields / fn-params / tagged-variants + // (sea-of-stars per rule 12). Phase 2 (#50b) retired cgenutil's + // AST-keyed nullableptrtag onto the chain. let head: *tparam = nil; let tail: *tparam = nil; let maxsz: u64 = 0u64; @@ -8440,13 +8456,33 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = { let v: *node = n.list; for (v != nil) { let vt: *tinfo = tinfofornode(c, v); - let tp: *tparam = alloc(tparam{name="", type_=vt, tnext=nil})!; - if (head == nil) { head = tp; } else { tail.tnext = tp; }; - tail = tp; if (vt != nil) { if (vt.size > maxsz) { maxsz = vt.size; }; if (vt.align > al) { al = vt.align; }; }; + let isspread: bool = (v.op == tkind.TK_ELLIPSIS); + let vu: *tinfo = vt; + if (isspread) { + if (vu != nil) { + if (vu.kind == tykind.TY_NAMED) { vu = vu.under; }; + }; + }; + if (isspread && vu != nil && vu.kind == tykind.TY_TAGGED) { + // Spliced variants carry the inner union's + // already-stamped iserror; no re-derivation. + let src: *tparam = vu.params; + for (src != nil) { + let tp: *tparam = alloc(tparam{name="", type_=src.type_, iserror=src.iserror, tnext=nil})!; + if (head == nil) { head = tp; } else { tail.tnext = tp; }; + tail = tp; + src = src.tnext; + }; + } else { + let ve: bool = varianterr(c, v); + let tp: *tparam = alloc(tparam{name="", type_=vt, iserror=ve, tnext=nil})!; + if (head == nil) { head = tp; } else { tail.tnext = tp; }; + tail = tp; + }; v = v.next; }; r.params = head; diff --git a/selfhost/cmd/wcc/check.ww b/selfhost/cmd/wcc/check.ww index b6cae1b6..6792fa16 100644 --- a/selfhost/cmd/wcc/check.ww +++ b/selfhost/cmd/wcc/check.ww @@ -1339,12 +1339,19 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = { r = newtype(tykind.TY_TAGGED); tinfocachebind(c.tc, n, r); // #50 / A.6.3f phase 1: populate ti.params as a tparam linked - // list (head=first source variant) in lock-step with the - // size/align accumulator. Mirrors cstage check.c:374-396 — - // same Tparam shape ww reuses across struct-fields / tuple- - // fields / fn-params / tagged-variants (sea-of-stars per rule - // 12). Phase 2 (#50b) retires cgenutil's AST-keyed - // nullableptrtag onto the chain. + // list (head=first source variant). #61a: flatten `...inner` + // tagged spreads into the chain and stamp each variant's + // iserror. Mirrors cstage check.c:366-389 — dealias one NAMED + // level, require TY_TAGGED, splice its (already-flattened) + // variants in declaration order; otherwise append the single + // variant. size/align stays accounted off the surface member + // (vt), so r.size is byte-identical to pre-#61a: the flatten + + // iserror are additive, with no #61a-stage readers (the variant + // machinery + cgwidentaggedstore/matchscrutt migrate onto the + // chain in #61b/c). Same shared Tparam shape ww reuses across + // struct-fields / tuple-fields / fn-params / tagged-variants + // (sea-of-stars per rule 12). Phase 2 (#50b) retired cgenutil's + // AST-keyed nullableptrtag onto the chain. let head: *tparam = nil; let tail: *tparam = nil; let maxsz: u64 = 0u64; @@ -1352,13 +1359,33 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = { let v: *node = n.list; for (v != nil) { let vt: *tinfo = tinfofornode(c, v); - let tp: *tparam = alloc(tparam{name="", type_=vt, tnext=nil})!; - if (head == nil) { head = tp; } else { tail.tnext = tp; }; - tail = tp; if (vt != nil) { if (vt.size > maxsz) { maxsz = vt.size; }; if (vt.align > al) { al = vt.align; }; }; + let isspread: bool = (v.op == tkind.TK_ELLIPSIS); + let vu: *tinfo = vt; + if (isspread) { + if (vu != nil) { + if (vu.kind == tykind.TY_NAMED) { vu = vu.under; }; + }; + }; + if (isspread && vu != nil && vu.kind == tykind.TY_TAGGED) { + // Spliced variants carry the inner union's + // already-stamped iserror; no re-derivation. + let src: *tparam = vu.params; + for (src != nil) { + let tp: *tparam = alloc(tparam{name="", type_=src.type_, iserror=src.iserror, tnext=nil})!; + if (head == nil) { head = tp; } else { tail.tnext = tp; }; + tail = tp; + src = src.tnext; + }; + } else { + let ve: bool = varianterr(c, v); + let tp: *tparam = alloc(tparam{name="", type_=vt, iserror=ve, tnext=nil})!; + if (head == nil) { head = tp; } else { tail.tnext = tp; }; + tail = tp; + }; v = v.next; }; r.params = head; diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index dd994544..9bf4859b 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -6401,6 +6401,15 @@ type tfield = struct { type tparam = struct { name: str, type_: *tinfo, + // #61a: per-variant `!T` error mark for TY_TAGGED variants. + // cstage-MIRROR divergence: harec carries no per-variant flag — + // it models `!T` as a distinct STORAGE_ERROR type node + // (ref/harec/include/types.h:144, src/types.c:151-159 + // type_is_error). wwstage tinfo has no iserror field + // (check.ww TTAGGED arm), so the bit rides the shared param + // struct instead, matching cstage Type.iserror semantics. + // Faithful STORAGE_ERROR-node port filed as #62. + iserror: bool, tnext: *tparam, }; @@ -8427,12 +8436,19 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = { r = newtype(tykind.TY_TAGGED); tinfocachebind(c.tc, n, r); // #50 / A.6.3f phase 1: populate ti.params as a tparam linked - // list (head=first source variant) in lock-step with the - // size/align accumulator. Mirrors cstage check.c:374-396 — - // same Tparam shape ww reuses across struct-fields / tuple- - // fields / fn-params / tagged-variants (sea-of-stars per rule - // 12). Phase 2 (#50b) retires cgenutil's AST-keyed - // nullableptrtag onto the chain. + // list (head=first source variant). #61a: flatten `...inner` + // tagged spreads into the chain and stamp each variant's + // iserror. Mirrors cstage check.c:366-389 — dealias one NAMED + // level, require TY_TAGGED, splice its (already-flattened) + // variants in declaration order; otherwise append the single + // variant. size/align stays accounted off the surface member + // (vt), so r.size is byte-identical to pre-#61a: the flatten + + // iserror are additive, with no #61a-stage readers (the variant + // machinery + cgwidentaggedstore/matchscrutt migrate onto the + // chain in #61b/c). Same shared Tparam shape ww reuses across + // struct-fields / tuple-fields / fn-params / tagged-variants + // (sea-of-stars per rule 12). Phase 2 (#50b) retired cgenutil's + // AST-keyed nullableptrtag onto the chain. let head: *tparam = nil; let tail: *tparam = nil; let maxsz: u64 = 0u64; @@ -8440,13 +8456,33 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = { let v: *node = n.list; for (v != nil) { let vt: *tinfo = tinfofornode(c, v); - let tp: *tparam = alloc(tparam{name="", type_=vt, tnext=nil})!; - if (head == nil) { head = tp; } else { tail.tnext = tp; }; - tail = tp; if (vt != nil) { if (vt.size > maxsz) { maxsz = vt.size; }; if (vt.align > al) { al = vt.align; }; }; + let isspread: bool = (v.op == tkind.TK_ELLIPSIS); + let vu: *tinfo = vt; + if (isspread) { + if (vu != nil) { + if (vu.kind == tykind.TY_NAMED) { vu = vu.under; }; + }; + }; + if (isspread && vu != nil && vu.kind == tykind.TY_TAGGED) { + // Spliced variants carry the inner union's + // already-stamped iserror; no re-derivation. + let src: *tparam = vu.params; + for (src != nil) { + let tp: *tparam = alloc(tparam{name="", type_=src.type_, iserror=src.iserror, tnext=nil})!; + if (head == nil) { head = tp; } else { tail.tnext = tp; }; + tail = tp; + src = src.tnext; + }; + } else { + let ve: bool = varianterr(c, v); + let tp: *tparam = alloc(tparam{name="", type_=vt, iserror=ve, tnext=nil})!; + if (head == nil) { head = tp; } else { tail.tnext = tp; }; + tail = tp; + }; v = v.next; }; r.params = head;