selfhost+cstage+test: graduate alias-chain unwrap to transitive (#22)
Single-peel TY_NAMED.under bottoms out at the inner alias when chain length is 2+, surfaces in two stages with different mechanisms: cstage's gates inline `if (t->kind == TY_NAMED) t = t->under` at every callsite (cgreturn, cglet sizing, cgexpr N_DOT, cgassign N_DOT, cg_sret_retsize) — graduated to a while-loop via new type_chase_named helper across 11 sites. wwstage routes all field-walks through structlookup, which registers only direct struct definitions (not aliases) — missing the alias-recurse fallback. New structlookupchain helper mirrors slotsize's N_TARRAY arm precedent; sretretsize + 4 cgenexpr.ww sites route through it. Splitting would either land cstage without unblocking wwstage's strings.tokenize wrapper shape (rule 10 byte-id regression) or land wwstage without cstage gate parity (breaking 995 self-rebuild). 756 sentinel exercises 4 rows × cstage RC + wwstage RC + byte-id = 12 fixtures; pre-fix rows 2 + 4 (slice-fields single alias, i32 double alias) fail on both RC and byte-id. The ~67 cstage / ~26 wwstage candidate sibling sites are #17-style structural-close follow-up; this commit fixes the immediate strings.tokenize-wrapper blockers.
This commit is contained in:
@@ -9150,6 +9150,39 @@ fn structnaturalsize(si: *structinfo) i32 = {
|
||||
// cg_sret_retsize predicate. Resolves N_TNAME → struct via structlookup
|
||||
// and unwraps one leading N_TBANG so `type box = !big;` still
|
||||
// triggers sret on the underlying big.
|
||||
//
|
||||
// Chain-of-aliases (#22): `type a = struct{...}; type b = a;` registers
|
||||
// `b → a` in c.aliases (target node = N_TNAME "a"), not `b → struct`.
|
||||
// When structlookup(c, "b") misses, fall through to aliaslookup and
|
||||
// recurse on the alias target — mirrors slotsize's N_TNAME arm
|
||||
// (cgenutil.ww:1955) and the cstage while-loop in cg_sret_retsize.
|
||||
|
||||
// structlookupchain — resolve TNAME `tn` to its registered struct,
|
||||
// chasing alias-of-alias (#22). Returns nil if the chain doesn't
|
||||
// bottom out at a struct. Mirrors cstage's transitive
|
||||
// `while (t->kind == TY_NAMED) t = t->under` peel; consumed by
|
||||
// cgdot / cgassign at every "field-walk on a struct-typed local"
|
||||
// site so a transitively-aliased struct name resolves to its
|
||||
// fieldinfo list regardless of chain depth.
|
||||
export fn structlookupchain(c: *cgen, tn: *node) *structinfo = {
|
||||
if (tn == nil) { return nil; };
|
||||
if (tn.kind != nkind.N_TNAME) { return nil; };
|
||||
let si: *structinfo = structlookup(c, tn.str);
|
||||
if (si != nil) { return si; };
|
||||
let cur: *node = tn;
|
||||
for (cur != nil && cur.kind == nkind.N_TNAME && si == nil) {
|
||||
let aliased: *node = aliaslookup(c, cur.str);
|
||||
if (aliased == nil) { cur = nil; }
|
||||
else {
|
||||
if (aliased.kind == nkind.N_TNAME) {
|
||||
si = structlookup(c, aliased.str);
|
||||
cur = aliased;
|
||||
} else { cur = nil; };
|
||||
};
|
||||
};
|
||||
return si;
|
||||
};
|
||||
|
||||
export fn sretretsize(c: *cgen, t: *node) i32 = {
|
||||
if (t == nil) { return 0; };
|
||||
let r: *node = t;
|
||||
@@ -9162,7 +9195,15 @@ export fn sretretsize(c: *cgen, t: *node) i32 = {
|
||||
if (primsize(r.str) > 0) { return 0; };
|
||||
if (streq(r.str, "str")) { return 0; };
|
||||
let si: *structinfo = structlookup(c, r.str);
|
||||
if (si == nil) { return 0; };
|
||||
if (si == nil) {
|
||||
if (c != nil) {
|
||||
let aliased: *node = aliaslookup(c, r.str);
|
||||
if (aliased != nil) {
|
||||
return sretretsize(c, aliased);
|
||||
};
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
let n: i32 = structnaturalsize(si);
|
||||
if (n <= 24) { return 0; };
|
||||
return n;
|
||||
@@ -12807,7 +12848,11 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain walks the alias chain on
|
||||
// a miss so `*tokenizer` where tokenizer is
|
||||
// a transitively-aliased struct still
|
||||
// resolves to the underlying fieldinfo (#22).
|
||||
let si: *structinfo = structlookupchain(c, inner);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
@@ -12890,8 +12935,11 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Direct struct local: field load at off+foff.
|
||||
if (lkind == nkind.N_TNAME) {
|
||||
let sname: str = tn.str;
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain walks the alias chain on
|
||||
// miss so a transitively-aliased struct (`type
|
||||
// b = a; a = struct`) still resolves to the
|
||||
// underlying fieldinfo (#22).
|
||||
let si: *structinfo = structlookupchain(c, tn);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
@@ -15556,7 +15604,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain (#22) handles the
|
||||
// alias-chain miss; same shape as the
|
||||
// cgdot pointer-to-struct read site.
|
||||
let si: *structinfo = structlookupchain(c, inner);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
@@ -15794,8 +15845,9 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Direct struct local: store at off+foff.
|
||||
if (lkind == nkind.N_TNAME) {
|
||||
let sname: str = tn.str;
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain (#22) — same shape
|
||||
// as the cgdot direct-local read site.
|
||||
let si: *structinfo = structlookupchain(c, tn);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
|
||||
@@ -1280,7 +1280,11 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain walks the alias chain on
|
||||
// a miss so `*tokenizer` where tokenizer is
|
||||
// a transitively-aliased struct still
|
||||
// resolves to the underlying fieldinfo (#22).
|
||||
let si: *structinfo = structlookupchain(c, inner);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
@@ -1363,8 +1367,11 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Direct struct local: field load at off+foff.
|
||||
if (lkind == nkind.N_TNAME) {
|
||||
let sname: str = tn.str;
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain walks the alias chain on
|
||||
// miss so a transitively-aliased struct (`type
|
||||
// b = a; a = struct`) still resolves to the
|
||||
// underlying fieldinfo (#22).
|
||||
let si: *structinfo = structlookupchain(c, tn);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
@@ -4029,7 +4036,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain (#22) handles the
|
||||
// alias-chain miss; same shape as the
|
||||
// cgdot pointer-to-struct read site.
|
||||
let si: *structinfo = structlookupchain(c, inner);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
@@ -4267,8 +4277,9 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Direct struct local: store at off+foff.
|
||||
if (lkind == nkind.N_TNAME) {
|
||||
let sname: str = tn.str;
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain (#22) — same shape
|
||||
// as the cgdot direct-local read site.
|
||||
let si: *structinfo = structlookupchain(c, tn);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
|
||||
@@ -1520,6 +1520,39 @@ fn structnaturalsize(si: *structinfo) i32 = {
|
||||
// cg_sret_retsize predicate. Resolves N_TNAME → struct via structlookup
|
||||
// and unwraps one leading N_TBANG so `type box = !big;` still
|
||||
// triggers sret on the underlying big.
|
||||
//
|
||||
// Chain-of-aliases (#22): `type a = struct{...}; type b = a;` registers
|
||||
// `b → a` in c.aliases (target node = N_TNAME "a"), not `b → struct`.
|
||||
// When structlookup(c, "b") misses, fall through to aliaslookup and
|
||||
// recurse on the alias target — mirrors slotsize's N_TNAME arm
|
||||
// (cgenutil.ww:1955) and the cstage while-loop in cg_sret_retsize.
|
||||
|
||||
// structlookupchain — resolve TNAME `tn` to its registered struct,
|
||||
// chasing alias-of-alias (#22). Returns nil if the chain doesn't
|
||||
// bottom out at a struct. Mirrors cstage's transitive
|
||||
// `while (t->kind == TY_NAMED) t = t->under` peel; consumed by
|
||||
// cgdot / cgassign at every "field-walk on a struct-typed local"
|
||||
// site so a transitively-aliased struct name resolves to its
|
||||
// fieldinfo list regardless of chain depth.
|
||||
export fn structlookupchain(c: *cgen, tn: *node) *structinfo = {
|
||||
if (tn == nil) { return nil; };
|
||||
if (tn.kind != nkind.N_TNAME) { return nil; };
|
||||
let si: *structinfo = structlookup(c, tn.str);
|
||||
if (si != nil) { return si; };
|
||||
let cur: *node = tn;
|
||||
for (cur != nil && cur.kind == nkind.N_TNAME && si == nil) {
|
||||
let aliased: *node = aliaslookup(c, cur.str);
|
||||
if (aliased == nil) { cur = nil; }
|
||||
else {
|
||||
if (aliased.kind == nkind.N_TNAME) {
|
||||
si = structlookup(c, aliased.str);
|
||||
cur = aliased;
|
||||
} else { cur = nil; };
|
||||
};
|
||||
};
|
||||
return si;
|
||||
};
|
||||
|
||||
export fn sretretsize(c: *cgen, t: *node) i32 = {
|
||||
if (t == nil) { return 0; };
|
||||
let r: *node = t;
|
||||
@@ -1532,7 +1565,15 @@ export fn sretretsize(c: *cgen, t: *node) i32 = {
|
||||
if (primsize(r.str) > 0) { return 0; };
|
||||
if (streq(r.str, "str")) { return 0; };
|
||||
let si: *structinfo = structlookup(c, r.str);
|
||||
if (si == nil) { return 0; };
|
||||
if (si == nil) {
|
||||
if (c != nil) {
|
||||
let aliased: *node = aliaslookup(c, r.str);
|
||||
if (aliased != nil) {
|
||||
return sretretsize(c, aliased);
|
||||
};
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
let n: i32 = structnaturalsize(si);
|
||||
if (n <= 24) { return 0; };
|
||||
return n;
|
||||
|
||||
@@ -9150,6 +9150,39 @@ fn structnaturalsize(si: *structinfo) i32 = {
|
||||
// cg_sret_retsize predicate. Resolves N_TNAME → struct via structlookup
|
||||
// and unwraps one leading N_TBANG so `type box = !big;` still
|
||||
// triggers sret on the underlying big.
|
||||
//
|
||||
// Chain-of-aliases (#22): `type a = struct{...}; type b = a;` registers
|
||||
// `b → a` in c.aliases (target node = N_TNAME "a"), not `b → struct`.
|
||||
// When structlookup(c, "b") misses, fall through to aliaslookup and
|
||||
// recurse on the alias target — mirrors slotsize's N_TNAME arm
|
||||
// (cgenutil.ww:1955) and the cstage while-loop in cg_sret_retsize.
|
||||
|
||||
// structlookupchain — resolve TNAME `tn` to its registered struct,
|
||||
// chasing alias-of-alias (#22). Returns nil if the chain doesn't
|
||||
// bottom out at a struct. Mirrors cstage's transitive
|
||||
// `while (t->kind == TY_NAMED) t = t->under` peel; consumed by
|
||||
// cgdot / cgassign at every "field-walk on a struct-typed local"
|
||||
// site so a transitively-aliased struct name resolves to its
|
||||
// fieldinfo list regardless of chain depth.
|
||||
export fn structlookupchain(c: *cgen, tn: *node) *structinfo = {
|
||||
if (tn == nil) { return nil; };
|
||||
if (tn.kind != nkind.N_TNAME) { return nil; };
|
||||
let si: *structinfo = structlookup(c, tn.str);
|
||||
if (si != nil) { return si; };
|
||||
let cur: *node = tn;
|
||||
for (cur != nil && cur.kind == nkind.N_TNAME && si == nil) {
|
||||
let aliased: *node = aliaslookup(c, cur.str);
|
||||
if (aliased == nil) { cur = nil; }
|
||||
else {
|
||||
if (aliased.kind == nkind.N_TNAME) {
|
||||
si = structlookup(c, aliased.str);
|
||||
cur = aliased;
|
||||
} else { cur = nil; };
|
||||
};
|
||||
};
|
||||
return si;
|
||||
};
|
||||
|
||||
export fn sretretsize(c: *cgen, t: *node) i32 = {
|
||||
if (t == nil) { return 0; };
|
||||
let r: *node = t;
|
||||
@@ -9162,7 +9195,15 @@ export fn sretretsize(c: *cgen, t: *node) i32 = {
|
||||
if (primsize(r.str) > 0) { return 0; };
|
||||
if (streq(r.str, "str")) { return 0; };
|
||||
let si: *structinfo = structlookup(c, r.str);
|
||||
if (si == nil) { return 0; };
|
||||
if (si == nil) {
|
||||
if (c != nil) {
|
||||
let aliased: *node = aliaslookup(c, r.str);
|
||||
if (aliased != nil) {
|
||||
return sretretsize(c, aliased);
|
||||
};
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
let n: i32 = structnaturalsize(si);
|
||||
if (n <= 24) { return 0; };
|
||||
return n;
|
||||
@@ -12807,7 +12848,11 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain walks the alias chain on
|
||||
// a miss so `*tokenizer` where tokenizer is
|
||||
// a transitively-aliased struct still
|
||||
// resolves to the underlying fieldinfo (#22).
|
||||
let si: *structinfo = structlookupchain(c, inner);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
@@ -12890,8 +12935,11 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Direct struct local: field load at off+foff.
|
||||
if (lkind == nkind.N_TNAME) {
|
||||
let sname: str = tn.str;
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain walks the alias chain on
|
||||
// miss so a transitively-aliased struct (`type
|
||||
// b = a; a = struct`) still resolves to the
|
||||
// underlying fieldinfo (#22).
|
||||
let si: *structinfo = structlookupchain(c, tn);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
@@ -15556,7 +15604,10 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
if (inner.kind == nkind.N_TNAME) { sname = inner.str; };
|
||||
};
|
||||
if (sname.len > 0) {
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain (#22) handles the
|
||||
// alias-chain miss; same shape as the
|
||||
// cgdot pointer-to-struct read site.
|
||||
let si: *structinfo = structlookupchain(c, inner);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
@@ -15794,8 +15845,9 @@ fn cgassign(c: *cgen, n: *node) void = {
|
||||
};
|
||||
// Direct struct local: store at off+foff.
|
||||
if (lkind == nkind.N_TNAME) {
|
||||
let sname: str = tn.str;
|
||||
let si: *structinfo = structlookup(c, sname);
|
||||
// structlookupchain (#22) — same shape
|
||||
// as the cgdot direct-local read site.
|
||||
let si: *structinfo = structlookupchain(c, tn);
|
||||
if (si != nil) {
|
||||
let fi: *fieldinfo = si.fields;
|
||||
for (fi != nil) {
|
||||
|
||||
Reference in New Issue
Block a user