w6c: forrange bind cap loud; drain widen gates keyed on type-eq (#55)
Two silent seams in one sweep. The for-range destructure silently dropped the 9th+ binding in both stages (bind arrays are sized 8); the cap now hard-stops per the DEFER_MAX/LOOP_MAX discipline (the shape is unreachable today -- every wide-tuple construction path already loud-stops). argtaggedwidensz, the drain-side SSoT for widened call args, still keyed "natural push" on slot-size equality while pushargsrev flipped to type equality with #55 -- a same-slot subset was classified widen by push, natural by drain (counts coincide today; a classification reader desyncs, the #48 shape). Both sides now share the wsame key; the widen-branch tag miss for a concrete source joins the task-2 loud-stop family (tagged subset sources keep t=0 -- their widentag is never read, the scratch store remaps). Lead 17 (enum fold-failure prev+1) DISPROVED: enumvalfold gates every member init in the checker and its fold set is op-for-op equal to cgen's enumevalmember, so the fallback only runs inside already-failing compiles.
This commit is contained in:
@@ -15209,7 +15209,13 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
|||||||
Tparam *tp = (etu && etu->kind == TY_TUPLE) ?
|
Tparam *tp = (etu && etu->kind == TY_TUPLE) ?
|
||||||
etu->params : NULL;
|
etu->params : NULL;
|
||||||
int field_off = 0;
|
int field_off = 0;
|
||||||
for (Node *nm = n->list; nm && nbinds < 8; nm = nm->next) {
|
for (Node *nm = n->list; nm; nm = nm->next) {
|
||||||
|
/* binds[] is sized 8; a 9th binding silently
|
||||||
|
* vanished pre-gate (rule 7 — the DEFER_MAX/
|
||||||
|
* LOOP_MAX cap discipline). */
|
||||||
|
if (nbinds >= 8)
|
||||||
|
fatal("for-range destructure: more "
|
||||||
|
"than 8 bindings (rule 7)");
|
||||||
int fsz = tp && tp->type ? (int)tp->type->size : 8;
|
int fsz = tp && tp->type ? (int)tp->type->size : 8;
|
||||||
int slot_sz = (fsz < 8) ? 8 : fsz;
|
int slot_sz = (fsz < 8) ? 8 : fsz;
|
||||||
binds[nbinds].sz = fsz;
|
binds[nbinds].sz = fsz;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package wwfixture;
|
package wwfixture;
|
||||||
|
|
||||||
def protocolversion: i32 = 1;
|
def protocolversion: i32 = 1;
|
||||||
def corpuscount: i32 = 1750;
|
def corpuscount: i32 = 1751;
|
||||||
def errorcount: i32 = 349;
|
def errorcount: i32 = 349;
|
||||||
def compilecount: i32 = 21;
|
def compilecount: i32 = 21;
|
||||||
def runcount: i32 = 209;
|
def runcount: i32 = 209;
|
||||||
def runexitcount: i32 = 1171;
|
def runexitcount: i32 = 1172;
|
||||||
def nativecount: i32 = 3500;
|
def nativecount: i32 = 3502;
|
||||||
def corpushash: str = "d52fa0df91c89bf51bca70045a2dbf39d5012ac03bf687e231240dc1c5b3fc53";
|
def corpushash: str = "0d0401f0d80ca682b0872343a62db4f91c0ff4c68e71f97cd5358cf92faffebc";
|
||||||
|
|
||||||
type directive = enum i32 {
|
type directive = enum i32 {
|
||||||
ERROR = 0,
|
ERROR = 0,
|
||||||
|
|||||||
@@ -3892,8 +3892,14 @@ fn cgforrange(c: *cgen, n: *syntax.node) void = {
|
|||||||
let field_off: i32 = 0;
|
let field_off: i32 = 0;
|
||||||
let m: *syntax.node = n.list;
|
let m: *syntax.node = n.list;
|
||||||
for (m != nil) {
|
for (m != nil) {
|
||||||
if (nbinds >= 8) { m = nil; }
|
// bind_* arrays are sized 8; a 9th binding silently
|
||||||
else {
|
// vanished pre-gate (rule 7 — the DEFER_MAX/LOOP_MAX
|
||||||
|
// cap discipline).
|
||||||
|
if (nbinds >= 8) {
|
||||||
|
let mfr: str = "for-range destructure: more than 8 bindings (rule 7)\n";
|
||||||
|
os.write(2, mfr.ptr, mfr.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
} else {
|
||||||
let fsz: i32 = 8;
|
let fsz: i32 = 8;
|
||||||
let signf: bool = false;
|
let signf: bool = false;
|
||||||
// tp walks the N_TPARAM wrapper chain; tpt is the
|
// tp walks the N_TPARAM wrapper chain; tpt is the
|
||||||
|
|||||||
@@ -293,25 +293,40 @@ fn argtaggedwidensz(c: *cgen, arg0: *syntax.node, param: *syntax.node) i32 = {
|
|||||||
if (taggedmemargsize(ptype.type_: *syntax.tinfo) > 0) { return 0; };
|
if (taggedmemargsize(ptype.type_: *syntax.tinfo) > 0) { return 0; };
|
||||||
let arg: *syntax.node = taggedcastpeel(c, arg0);
|
let arg: *syntax.node = taggedcastpeel(c, arg0);
|
||||||
let pslot: i32 = slotsize(c, ptype);
|
let pslot: i32 = slotsize(c, ptype);
|
||||||
// Already a matching-slot tagged source → natural push, no widen
|
// "Already tagged, natural push" is TYPE equality with the param,
|
||||||
// (mirrors pushargsrev's aistagged gates: ident/call/index/dot/deref).
|
// never slot-size equality (#55) — a same-slot SUBSET is classified
|
||||||
|
// widen by pushargsrev, so the drain SSoT must agree word-for-word
|
||||||
|
// (word counts coincide for same-slot subsets, but a classification
|
||||||
|
// reader desyncs — the #48 float-misclassify shape). Same wsame key
|
||||||
|
// as pushargsrev's aistagged gates.
|
||||||
|
let wpt: *syntax.tinfo = ptype.type_: *syntax.tinfo;
|
||||||
|
let wat: *syntax.tinfo = arg.type_: *syntax.tinfo;
|
||||||
|
let wpu: *syntax.tinfo = tichase(wpt);
|
||||||
|
let wau: *syntax.tinfo = tichase(wat);
|
||||||
|
let wsame: bool = false;
|
||||||
|
if (wpu != nil && wpu == wau) { wsame = true; };
|
||||||
|
if (!wsame && wpt != nil && wat != nil) {
|
||||||
|
if (syntax.typeeq(wpt, wat)) { wsame = true; };
|
||||||
|
};
|
||||||
if (arg.kind == syntax.nkind.N_IDENT) {
|
if (arg.kind == syntax.nkind.N_IDENT) {
|
||||||
let lc: *local = localfindnode(c, arg.str);
|
let lc: *local = localfindnode(c, arg.str);
|
||||||
if (lc != nil) {
|
if (lc != nil) {
|
||||||
if (istaggedtype(c, lc.tnode)) {
|
if (istaggedtype(c, lc.tnode)) {
|
||||||
if (slotsize(c, lc.tnode) == pslot) { return 0; };
|
if (wsame) { return 0; };
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
if (taggedcallslot(c, arg) == pslot) { return 0; };
|
if (taggedcallslot(c, arg) > 0) {
|
||||||
|
if (wsame) { return 0; };
|
||||||
|
};
|
||||||
if (arg.kind == syntax.nkind.N_INDEX) {
|
if (arg.kind == syntax.nkind.N_INDEX) {
|
||||||
if (istaggedtype(c, arg)) { if (slotsize(c, arg) == pslot) { return 0; }; };
|
if (istaggedtype(c, arg)) { if (wsame) { return 0; }; };
|
||||||
};
|
};
|
||||||
if (arg.kind == syntax.nkind.N_DOT) {
|
if (arg.kind == syntax.nkind.N_DOT) {
|
||||||
if (istaggedtype(c, arg)) { if (slotsize(c, arg) == pslot) { return 0; }; };
|
if (istaggedtype(c, arg)) { if (wsame) { return 0; }; };
|
||||||
};
|
};
|
||||||
if (arg.kind == syntax.nkind.N_UN && arg.op == syntax.tkind.TK_STAR) {
|
if (arg.kind == syntax.nkind.N_UN && arg.op == syntax.tkind.TK_STAR) {
|
||||||
if (istaggedtype(c, arg)) { if (slotsize(c, arg) == pslot) { return 0; }; };
|
if (istaggedtype(c, arg)) { if (wsame) { return 0; }; };
|
||||||
};
|
};
|
||||||
// The 8B nullable fold pushes one pointer word the scalar drain path
|
// The 8B nullable fold pushes one pointer word the scalar drain path
|
||||||
// already pops correctly; only the multi-word tagged widen needs the
|
// already pops correctly; only the multi-word tagged widen needs the
|
||||||
@@ -574,6 +589,15 @@ fn pushargsrev(c: *cgen, arg: *syntax.node, param: *syntax.node, memphase: bool,
|
|||||||
widensz = slotsize(c, ptype);
|
widensz = slotsize(c, ptype);
|
||||||
let tagged: *syntax.node = resolvetagged(c, ptype);
|
let tagged: *syntax.node = resolvetagged(c, ptype);
|
||||||
let t: i32 = taggedvariantindex(c, tagged, arg);
|
let t: i32 = taggedvariantindex(c, tagged, arg);
|
||||||
|
// tagged→tagged subset sources synthesize
|
||||||
|
// no scalar tag here (the widen store
|
||||||
|
// remaps); only a CONCRETE source with no
|
||||||
|
// variant is the rule-7 miss.
|
||||||
|
if (t < 0 && !istaggedtype(c, arg)) {
|
||||||
|
let mwt: str = "tagged widen: no variant tag for source (rule 7)\n";
|
||||||
|
os.write(2, mwt.ptr, mwt.len: u64);
|
||||||
|
os.exit(1);
|
||||||
|
};
|
||||||
if (t < 0) { t = 0; };
|
if (t < 0) { t = 0; };
|
||||||
widentag = t;
|
widentag = t;
|
||||||
};
|
};
|
||||||
|
|||||||
17
test/wcc/data/subset_arg_widen/case.ww
Normal file
17
test/wcc/data/subset_arg_widen/case.ww
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
//ww:run-exit 0
|
||||||
|
// #55 drain-SSoT leg: a same-slot subset ((bool|void) -> (i64|bool|void),
|
||||||
|
// both 16B) call arg is classified WIDEN by push AND drain — the old
|
||||||
|
// slot-size drain gates called it natural. The 0->1 tag remap through
|
||||||
|
// the widen scratch is what this pins.
|
||||||
|
package main;
|
||||||
|
type narrow = (bool | void);
|
||||||
|
type wide = (i64 | bool | void);
|
||||||
|
fn take(w: wide) i32 = {
|
||||||
|
if (w is bool) { return 7; };
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
export fn main() i32 = {
|
||||||
|
let n: narrow = true;
|
||||||
|
if (take(n) != 7) { return 1; };
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user