wcc/cgen: S1 loud-stop subset-cast tagged widen in let+return (wwstage align to cstage #35)
A widening sub-union cast (return/let `v: inner` into a union whose member is the nested `inner`) skipped the nested widen arm and emitted tag=0 (a 2nd-variant value returned the wrong payload). cstage loud-stops #35; wwstage now matches: cgwidentaggedstorebp louds on the subset cast, and cgreturn routes genuine-widening tagged returns (rhs TY_TAGGED, ru1!=fu1) through the widener choke-point so the same loud fires. Same-type returns stay on the proven passthrough. Faithful inner-tag->outer-index remap deferred. test/wcc/835 S1 row.
This commit is contained in:
@@ -20350,6 +20350,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
|||||||
// NAMED tinfo (check.ww:1160-1173) so typeeq hits
|
// NAMED tinfo (check.ww:1160-1173) so typeeq hits
|
||||||
// the a==b fast path.
|
// the a==b fast path.
|
||||||
let castisdst: bool = false;
|
let castisdst: bool = false;
|
||||||
|
let castsubset: bool = false;
|
||||||
let castrhs: *node = src.rhs;
|
let castrhs: *node = src.rhs;
|
||||||
if (castrhs != nil) {
|
if (castrhs != nil) {
|
||||||
let castt: *tinfo = castrhs.type_: *tinfo;
|
let castt: *tinfo = castrhs.type_: *tinfo;
|
||||||
@@ -20361,10 +20362,30 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
|||||||
} else { if (castu.kind == tykind.TY_TAGGED) {
|
} else { if (castu.kind == tykind.TY_TAGGED) {
|
||||||
if (typeeq(castt, dst)) {
|
if (typeeq(castt, dst)) {
|
||||||
castisdst = true;
|
castisdst = true;
|
||||||
|
} else {
|
||||||
|
castsubset = true;
|
||||||
};
|
};
|
||||||
}; };
|
}; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// S1/#35 (rule 7): a widen-SUBSET cast — the cast target
|
||||||
|
// is a TAGGED union that is NOT dst (castu tagged &&
|
||||||
|
// !typeeq(castt, dst)). The inner-variant tag is never
|
||||||
|
// remapped to dst's index, so the scalar arm below would
|
||||||
|
// emit tag=0: a SILENT mis-tag on any 2nd-variant value
|
||||||
|
// (census S1: `return true: inner`, inner=(int|bool) into
|
||||||
|
// (int|bool|str), ran the int arm not the bool arm). Loud-
|
||||||
|
// align to cstage cgen.c:2721-2723. The !inneristagged gate
|
||||||
|
// matches the src=inner collapse below — a tagged inner is
|
||||||
|
// handled by the #218 nested arm. Faithful remap (read inner
|
||||||
|
// tag, inner-idx→outer-idx) = the #23/#40 widen-subset
|
||||||
|
// feature, deferred post-CSP (needs the nominal variant-
|
||||||
|
// remap table).
|
||||||
|
if (castsubset && !inneristagged) {
|
||||||
|
let m35: str = "#35: tagged cast source shape unwired at the widen subset arm (rule 7)\n";
|
||||||
|
os.write(2, m35.ptr, m35.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
if (castisdst && !inneristagged) {
|
if (castisdst && !inneristagged) {
|
||||||
src = inner;
|
src = inner;
|
||||||
};
|
};
|
||||||
@@ -34980,6 +35001,29 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
|||||||
if (taggedmemread(c, rhs)) {
|
if (taggedmemread(c, rhs)) {
|
||||||
needswiden = true;
|
needswiden = true;
|
||||||
};
|
};
|
||||||
|
// S1/#35: a GENUINE-WIDENING tagged source
|
||||||
|
// (stamped type_ TY_TAGGED and != fnret;
|
||||||
|
// exact-type rides forwardtagged/plain above)
|
||||||
|
// routes through the widener — tag remap for
|
||||||
|
// ident/call (#218), #35 widen-subset loud for
|
||||||
|
// cast/dot. Mirrors cstage cgreturn istagged→
|
||||||
|
// cg_widen_tagged_store (cmd/w6c/cgen.c:13008-
|
||||||
|
// 13011 → :2721). The ru1 != fu1 exclusion keeps
|
||||||
|
// EXACT-type tagged casts on cstage's passthrough
|
||||||
|
// (forwardtagged's own ru==fu equality) so byte-id
|
||||||
|
// holds.
|
||||||
|
let ru1: *tinfo = rhs.type_: *tinfo;
|
||||||
|
ru1 = tichase(ru1);
|
||||||
|
let fu1: *tinfo = nil;
|
||||||
|
if (c.fnret != nil) {
|
||||||
|
fu1 = c.fnret.type_: *tinfo;
|
||||||
|
fu1 = tichase(fu1);
|
||||||
|
};
|
||||||
|
if (ru1 != nil && fu1 != nil) {
|
||||||
|
if (ru1.kind == tykind.TY_TAGGED && ru1 != fu1) {
|
||||||
|
needswiden = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (needswiden) {
|
if (needswiden) {
|
||||||
|
|||||||
@@ -1037,6 +1037,29 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
|||||||
if (taggedmemread(c, rhs)) {
|
if (taggedmemread(c, rhs)) {
|
||||||
needswiden = true;
|
needswiden = true;
|
||||||
};
|
};
|
||||||
|
// S1/#35: a GENUINE-WIDENING tagged source
|
||||||
|
// (stamped type_ TY_TAGGED and != fnret;
|
||||||
|
// exact-type rides forwardtagged/plain above)
|
||||||
|
// routes through the widener — tag remap for
|
||||||
|
// ident/call (#218), #35 widen-subset loud for
|
||||||
|
// cast/dot. Mirrors cstage cgreturn istagged→
|
||||||
|
// cg_widen_tagged_store (cmd/w6c/cgen.c:13008-
|
||||||
|
// 13011 → :2721). The ru1 != fu1 exclusion keeps
|
||||||
|
// EXACT-type tagged casts on cstage's passthrough
|
||||||
|
// (forwardtagged's own ru==fu equality) so byte-id
|
||||||
|
// holds.
|
||||||
|
let ru1: *tinfo = rhs.type_: *tinfo;
|
||||||
|
ru1 = tichase(ru1);
|
||||||
|
let fu1: *tinfo = nil;
|
||||||
|
if (c.fnret != nil) {
|
||||||
|
fu1 = c.fnret.type_: *tinfo;
|
||||||
|
fu1 = tichase(fu1);
|
||||||
|
};
|
||||||
|
if (ru1 != nil && fu1 != nil) {
|
||||||
|
if (ru1.kind == tykind.TY_TAGGED && ru1 != fu1) {
|
||||||
|
needswiden = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (needswiden) {
|
if (needswiden) {
|
||||||
|
|||||||
@@ -3838,6 +3838,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
|||||||
// NAMED tinfo (check.ww:1160-1173) so typeeq hits
|
// NAMED tinfo (check.ww:1160-1173) so typeeq hits
|
||||||
// the a==b fast path.
|
// the a==b fast path.
|
||||||
let castisdst: bool = false;
|
let castisdst: bool = false;
|
||||||
|
let castsubset: bool = false;
|
||||||
let castrhs: *node = src.rhs;
|
let castrhs: *node = src.rhs;
|
||||||
if (castrhs != nil) {
|
if (castrhs != nil) {
|
||||||
let castt: *tinfo = castrhs.type_: *tinfo;
|
let castt: *tinfo = castrhs.type_: *tinfo;
|
||||||
@@ -3849,10 +3850,30 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
|||||||
} else { if (castu.kind == tykind.TY_TAGGED) {
|
} else { if (castu.kind == tykind.TY_TAGGED) {
|
||||||
if (typeeq(castt, dst)) {
|
if (typeeq(castt, dst)) {
|
||||||
castisdst = true;
|
castisdst = true;
|
||||||
|
} else {
|
||||||
|
castsubset = true;
|
||||||
};
|
};
|
||||||
}; };
|
}; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// S1/#35 (rule 7): a widen-SUBSET cast — the cast target
|
||||||
|
// is a TAGGED union that is NOT dst (castu tagged &&
|
||||||
|
// !typeeq(castt, dst)). The inner-variant tag is never
|
||||||
|
// remapped to dst's index, so the scalar arm below would
|
||||||
|
// emit tag=0: a SILENT mis-tag on any 2nd-variant value
|
||||||
|
// (census S1: `return true: inner`, inner=(int|bool) into
|
||||||
|
// (int|bool|str), ran the int arm not the bool arm). Loud-
|
||||||
|
// align to cstage cgen.c:2721-2723. The !inneristagged gate
|
||||||
|
// matches the src=inner collapse below — a tagged inner is
|
||||||
|
// handled by the #218 nested arm. Faithful remap (read inner
|
||||||
|
// tag, inner-idx→outer-idx) = the #23/#40 widen-subset
|
||||||
|
// feature, deferred post-CSP (needs the nominal variant-
|
||||||
|
// remap table).
|
||||||
|
if (castsubset && !inneristagged) {
|
||||||
|
let m35: str = "#35: tagged cast source shape unwired at the widen subset arm (rule 7)\n";
|
||||||
|
os.write(2, m35.ptr, m35.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
if (castisdst && !inneristagged) {
|
if (castisdst && !inneristagged) {
|
||||||
src = inner;
|
src = inner;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20350,6 +20350,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
|||||||
// NAMED tinfo (check.ww:1160-1173) so typeeq hits
|
// NAMED tinfo (check.ww:1160-1173) so typeeq hits
|
||||||
// the a==b fast path.
|
// the a==b fast path.
|
||||||
let castisdst: bool = false;
|
let castisdst: bool = false;
|
||||||
|
let castsubset: bool = false;
|
||||||
let castrhs: *node = src.rhs;
|
let castrhs: *node = src.rhs;
|
||||||
if (castrhs != nil) {
|
if (castrhs != nil) {
|
||||||
let castt: *tinfo = castrhs.type_: *tinfo;
|
let castt: *tinfo = castrhs.type_: *tinfo;
|
||||||
@@ -20361,10 +20362,30 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
|||||||
} else { if (castu.kind == tykind.TY_TAGGED) {
|
} else { if (castu.kind == tykind.TY_TAGGED) {
|
||||||
if (typeeq(castt, dst)) {
|
if (typeeq(castt, dst)) {
|
||||||
castisdst = true;
|
castisdst = true;
|
||||||
|
} else {
|
||||||
|
castsubset = true;
|
||||||
};
|
};
|
||||||
}; };
|
}; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
// S1/#35 (rule 7): a widen-SUBSET cast — the cast target
|
||||||
|
// is a TAGGED union that is NOT dst (castu tagged &&
|
||||||
|
// !typeeq(castt, dst)). The inner-variant tag is never
|
||||||
|
// remapped to dst's index, so the scalar arm below would
|
||||||
|
// emit tag=0: a SILENT mis-tag on any 2nd-variant value
|
||||||
|
// (census S1: `return true: inner`, inner=(int|bool) into
|
||||||
|
// (int|bool|str), ran the int arm not the bool arm). Loud-
|
||||||
|
// align to cstage cgen.c:2721-2723. The !inneristagged gate
|
||||||
|
// matches the src=inner collapse below — a tagged inner is
|
||||||
|
// handled by the #218 nested arm. Faithful remap (read inner
|
||||||
|
// tag, inner-idx→outer-idx) = the #23/#40 widen-subset
|
||||||
|
// feature, deferred post-CSP (needs the nominal variant-
|
||||||
|
// remap table).
|
||||||
|
if (castsubset && !inneristagged) {
|
||||||
|
let m35: str = "#35: tagged cast source shape unwired at the widen subset arm (rule 7)\n";
|
||||||
|
os.write(2, m35.ptr, m35.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
if (castisdst && !inneristagged) {
|
if (castisdst && !inneristagged) {
|
||||||
src = inner;
|
src = inner;
|
||||||
};
|
};
|
||||||
@@ -34980,6 +35001,29 @@ fn cgreturn(c: *cgen, n: *node) void = {
|
|||||||
if (taggedmemread(c, rhs)) {
|
if (taggedmemread(c, rhs)) {
|
||||||
needswiden = true;
|
needswiden = true;
|
||||||
};
|
};
|
||||||
|
// S1/#35: a GENUINE-WIDENING tagged source
|
||||||
|
// (stamped type_ TY_TAGGED and != fnret;
|
||||||
|
// exact-type rides forwardtagged/plain above)
|
||||||
|
// routes through the widener — tag remap for
|
||||||
|
// ident/call (#218), #35 widen-subset loud for
|
||||||
|
// cast/dot. Mirrors cstage cgreturn istagged→
|
||||||
|
// cg_widen_tagged_store (cmd/w6c/cgen.c:13008-
|
||||||
|
// 13011 → :2721). The ru1 != fu1 exclusion keeps
|
||||||
|
// EXACT-type tagged casts on cstage's passthrough
|
||||||
|
// (forwardtagged's own ru==fu equality) so byte-id
|
||||||
|
// holds.
|
||||||
|
let ru1: *tinfo = rhs.type_: *tinfo;
|
||||||
|
ru1 = tichase(ru1);
|
||||||
|
let fu1: *tinfo = nil;
|
||||||
|
if (c.fnret != nil) {
|
||||||
|
fu1 = c.fnret.type_: *tinfo;
|
||||||
|
fu1 = tichase(fu1);
|
||||||
|
};
|
||||||
|
if (ru1 != nil && fu1 != nil) {
|
||||||
|
if (ru1.kind == tykind.TY_TAGGED && ru1 != fu1) {
|
||||||
|
needswiden = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (needswiden) {
|
if (needswiden) {
|
||||||
|
|||||||
@@ -15,11 +15,23 @@
|
|||||||
* (enum)=true) — A3 is the trap: dropping it would flip an enum-variant
|
* (enum)=true) — A3 is the trap: dropping it would flip an enum-variant
|
||||||
* union to reject while cstage accepts. wwstage-ONLY; check.c unchanged.
|
* union to reject while cstage accepts. wwstage-ONLY; check.c unchanged.
|
||||||
*
|
*
|
||||||
|
* S1 / #35 (ken census .ai/miscompile-census.md) — a widen-SUBSET cast in
|
||||||
|
* a tagged construct: `return true: inner` where inner=(int|bool) boxed
|
||||||
|
* into outer=(int|bool|str). The cast target is a TAGGED union that is NOT
|
||||||
|
* dst, and the inner-variant tag is never remapped to dst's index, so ww
|
||||||
|
* cgen's scalar arm emitted tag=0 — running the int arm on a bool value
|
||||||
|
* (census S1 returned 70, expected 11). cstage loud-stops at cgen.c:2721-
|
||||||
|
* 2723 (#35). wwstage gains the twin in cgwidentaggedstorebp (cgenutil.ww).
|
||||||
|
* A 2ND-VARIANT value (bool) is mandatory: `5:inner` is benign (int is
|
||||||
|
* variant 0 in both, tag 0 coincidentally right). Faithful inner->outer
|
||||||
|
* tag remap = the #23/#40 widen-subset feature, deferred post-CSP.
|
||||||
|
*
|
||||||
* neg row | shape | gate
|
* neg row | shape | gate
|
||||||
* -------------+------------------------------------------------+------
|
* -------------+------------------------------------------------+------
|
||||||
* R1_nested | inner=(int|bool); outer=(inner|str); ret 5 | FAIL
|
* R1_nested | inner=(int|bool); outer=(inner|str); ret 5 | FAIL
|
||||||
* R2_noslot | u=(str|bool); ret 5 | FAIL
|
* R2_noslot | u=(str|bool); ret 5 | FAIL
|
||||||
* R3_struct | A=struct{v:int}; u=(A|str); ret 5 | FAIL
|
* R3_struct | A=struct{v:int}; u=(A|str); ret 5 | FAIL
|
||||||
|
* S1_subcast | return true: inner (inner=(int|bool)->outer) | FAIL
|
||||||
*
|
*
|
||||||
* pos row | shape | want
|
* pos row | shape | want
|
||||||
* -------------+------------------------------------------------+------
|
* -------------+------------------------------------------------+------
|
||||||
@@ -30,7 +42,7 @@
|
|||||||
* A5_subset | sub=(int|uint); sup=(int|uint|str); ret x | 0
|
* A5_subset | sub=(int|uint); sup=(int|uint|str); ret x | 0
|
||||||
*
|
*
|
||||||
* Diagnostic TEXT is byte-id-blind (#23 routes through errnotassign "not
|
* Diagnostic TEXT is byte-id-blind (#23 routes through errnotassign "not
|
||||||
* assignable"); both stages REJECT and emit no asm.
|
* assignable"; S1 emits the #35 line); both stages REJECT and emit no asm.
|
||||||
* selfhost has no such construct, so 990-997 byte-id is untouched.
|
* selfhost has no such construct, so 990-997 byte-id is untouched.
|
||||||
*/
|
*/
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -127,6 +139,25 @@ static const char *neg[] = {
|
|||||||
"type u = (A | str);\n"
|
"type u = (A | str);\n"
|
||||||
"fn mk() u = { return 5; };\n"
|
"fn mk() u = { return 5; };\n"
|
||||||
"export fn main() i32 = { return 0; };\n",
|
"export fn main() i32 = { return 0; };\n",
|
||||||
|
/* S1 / #35 — widen-SUBSET cast in a RETURN, 2nd-variant (bool) value
|
||||||
|
* (census sx4). ww cgen's cgreturn formerly skipped the widener for an
|
||||||
|
* N_CAST-tagged source (needswiden=false) -> plain cgexpr -> tag=0
|
||||||
|
* SILENT (ran the int arm -> 70, want 11). Now the genuine-widening
|
||||||
|
* (ru!=fnret) needswiden arm routes it through cgwidentaggedstore ->
|
||||||
|
* the #35 widen-subset loud-stop. Both stages reject. */
|
||||||
|
"package main;\n"
|
||||||
|
"type inner = (int | bool);\n"
|
||||||
|
"type outer = (int | bool | str);\n"
|
||||||
|
"fn mk() outer = { return true: inner; };\n"
|
||||||
|
"export fn main() i32 = {\n"
|
||||||
|
"\tlet v = mk();\n"
|
||||||
|
"\tmatch (v) {\n"
|
||||||
|
"\tcase let n: int => return 70;\n"
|
||||||
|
"\tcase let b: bool => return 11;\n"
|
||||||
|
"\tcase let s: str => return 22;\n"
|
||||||
|
"\t};\n"
|
||||||
|
"\treturn 99;\n"
|
||||||
|
"};\n",
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
static int
|
||||||
|
|||||||
Reference in New Issue
Block a user