selfhost+cstage+test: graduate deflookup/deflookuprhs same-module-first (#4c)
Class A silent miscompile, latent until two modules export the same str-typed def leaf name and the .ptr/.len field-fold path consumes the wrong-module strlit address/length. Wwstage's deflookuprhs (selfhost/cmd/wcc/cgen.ww) walked c.defs head-first by dname; cgdot's .ptr/.len field-fold handed it the bare leaf from N_IDENT.str, silently inlining the wrong-module strlit. Cstage carries the same shape at cmd/w6c/cgen.c (Sdef walk #3 N_DOT field-fold): Sdef keyed by name only, head-pick on every cross-module collision. No in-tree corpus declares two same-leaf str defs, so 995_self_rebuild stayed green (same surfacing pattern as #4a enumlookup post-strings and #4b structlookup). Sixth leaf of the trio leaf-name lookup graduation (after #27 aliaslookup, #28 fnparams, #31 fnret, #4a enum, #4b struct). Same bundle precedent as #4a (which bundled wwstage enumlookup + enumlookupmod + cstage scope_lookup_prefer sister fix under one structural concern): four sister changes ship together. - defent +dmod field; collectdefs captures d.module. - wwstage deflookup two-pass walk — cosmetic (bool return is invariant under head-pick vs same-module-first), kept for structural symmetry with deflookuprhs. - wwstage deflookuprhs two-pass walk — load-bearing for the .ptr/.len field fold. - cstage Sdef +mod field; sdef_collect captures d->module raw (matches cgfn's raw cur_mod convention); new sdef_mod_match helper handles NULL-safe strcmp; cstage Sdef walk #3 N_DOT field-fold graduation (sister of wwstage deflookuprhs). Two additional cstage Sdef walks (N_IDENT bare load + N_DOT mod- qualified fallback) are DEFERRED. Both consume wwstage's cgenexpr.ww:553 path which is independently broken (str-def bare/ qualified reference emits MOVQ symname(SB) where strlit-inline is required); sentinel rows for those walks fail cs-vs-ws byte-id regardless of the cstage prefer-pass behavior. Per rule 7 the prefer-pass cannot ship without sentinels. Filed: task #11 (cstage walk #2 also needs n->lhs->str as hint source rather than cur_mod, matching #4a/#28/#31's *mod variant pattern) + task #12 (wwstage str-def symbol-load fix that unblocks both deferrals). 735_def_modshadow pins the fix with 1 row: bare-leaf .len of MSG in module alpha must fold against alpha's own def MSG (strlit length 41) even with beta's same-leaf 27-char def MSG at the head of c.defs / sdefs. Asserts the matching immediate inside the right TEXT sym + bad_imm anti-check on both stages plus byte-id between stages.
This commit is contained in:
@@ -1635,6 +1635,7 @@ fn fnparamslookupmod(c: *cgen, name: str, mod: str) *node = {
|
||||
|
||||
type defent = struct {
|
||||
dname: str,
|
||||
dmod: str, // originating module (`// MODULE: foo`), or empty
|
||||
drhs: *node,
|
||||
dnext: *defent,
|
||||
};
|
||||
@@ -1644,8 +1645,9 @@ fn collectdefs(c: *cgen, file: *node) void = {
|
||||
let d: *node = file.list;
|
||||
for (d != nil) {
|
||||
if (d.kind == nkind.N_DEF) {
|
||||
let e: *defent = amalloc(c.a, 32u64): *defent;
|
||||
let e: *defent = amalloc(c.a, 64u64): *defent;
|
||||
e.dname = d.str;
|
||||
e.dmod = d.module;
|
||||
e.drhs = d.rhs;
|
||||
e.dnext = c.defs;
|
||||
c.defs = e;
|
||||
@@ -1654,24 +1656,43 @@ fn collectdefs(c: *cgen, file: *node) void = {
|
||||
};
|
||||
};
|
||||
|
||||
// Same-module-first walk, then any. Trio-leaf graduation mirroring
|
||||
// aliaslookup (#27) and enum/structlookup (#4a/#4b): bool answer is
|
||||
// invariant either way, but the structural shape mirrors deflookuprhs
|
||||
// where the entry's drhs IS module-sensitive.
|
||||
fn deflookup(c: *cgen, name: str) bool = {
|
||||
let e: *defent = c.defs;
|
||||
for (e != nil) {
|
||||
let dn: str = e.dname;
|
||||
if (streq(dn, name)) { return true; };
|
||||
if (streq(e.dname, name)) {
|
||||
if (streq(e.dmod, c.curmod)) { return true; };
|
||||
};
|
||||
e = e.dnext;
|
||||
};
|
||||
e = c.defs;
|
||||
for (e != nil) {
|
||||
if (streq(e.dname, name)) { return true; };
|
||||
e = e.dnext;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// Returns the rhs init node for a top-level `def`, or nil if `name`
|
||||
// doesn't name a def. Used by cgdot to inline `.ptr`/`.len` on
|
||||
// doesn't name a def. Same-module-first walk: without the prefer pass
|
||||
// `MSG.ptr`/`MSG.len` in module M can collapse onto another module's
|
||||
// same-leaf `def MSG: str = ...` sitting at the head of c.defs and
|
||||
// inline the wrong strlit. Used by cgdot to inline `.ptr`/`.len` on
|
||||
// `def NAME: str = "..."` — those aren't laid out in memory.
|
||||
fn deflookuprhs(c: *cgen, name: str) *node = {
|
||||
let e: *defent = c.defs;
|
||||
for (e != nil) {
|
||||
let dn: str = e.dname;
|
||||
if (streq(dn, name)) { return e.drhs; };
|
||||
if (streq(e.dname, name)) {
|
||||
if (streq(e.dmod, c.curmod)) { return e.drhs; };
|
||||
};
|
||||
e = e.dnext;
|
||||
};
|
||||
e = c.defs;
|
||||
for (e != nil) {
|
||||
if (streq(e.dname, name)) { return e.drhs; };
|
||||
e = e.dnext;
|
||||
};
|
||||
return nil;
|
||||
|
||||
Reference in New Issue
Block a user