selfhost+test: graduate structlookup same-module-first (#4b)
Class A silent miscompile, latent until two modules export the same struct leaf name. Wwstage's structlookup (selfhost/cmd/wcc/cgenutil.ww) walked c.structs head-first by sname, returning the FIRST match. cgdot's *struct field-load branch handed it inner.str (the bare leaf from a parsed N_TPTR whose inner is N_TNAME) and the head-pick silently emitted the wrong-module field offset — a displacement against BX that loaded whatever the colliding-module struct happened to align there. Cstage carries no sister bug: resolve_typename (cmd/wcc/check.c:65) already routes bare-leaf TY_STRUCT names through scope_lookup_prefer per c->cur_mod, and cgen.c reads fi.foff off the typed Sym — cs vs ws asm diverged on every bare- leaf collision but no in-tree corpus declares two same-leaf structs, so 995_self_rebuild stayed green (same surfacing pattern as #4a enumlookup post-strings). Fifth leaf of the trio leaf-name lookup graduation (after #27 aliaslookup, #28/#31 fnparams/fnretlookupmod, #4a enumlookup): structlookup grows a same-module-first walk before the head-walk fallback, mirroring aliaslookup's two-pass shape. No structlookupmod variant — pkg.S collapses at parse time (lib/ww/parse/parse.ww joindotted) into a single N_TNAME str routed through the existing embedded-dot smod==pkg branch, so there's no cgdot-style N_DOT consumer surface to add a *mod variant for (deferred per rob until one surfaces). No cstage symmetric fix needed for the same reason the bug doesn't surface there. 734_struct_modshadow pins the fix with 2 rows: row 1 bare-leaf in module M must fold against M's own S even with another module's same-leaf S at the head of c.structs (asserts the matching field- load disp inside the right TEXT sym + bad disp NOT-presence anti- check + byte-id between stages); row 2 pkg-qualified alpha.S from inside alpha is defensive coverage of the pre-existing embedded-dot smod==pkg branch — same path pre/post-fix (no sentinel-flip on this commit), pinned here so a future regression to the embedded-dot lookup is caught.
This commit is contained in:
@@ -1430,19 +1430,28 @@ export fn callsretsize(c: *cgen, n: *node) i32 = {
|
||||
};
|
||||
|
||||
fn structlookup(c: *cgen, name: str) *structinfo = {
|
||||
// Exact match first: bare-from-source struct names and already-
|
||||
// leafed lookups hit here directly.
|
||||
// Same-module first, then any. Trio-leaf graduation mirroring
|
||||
// aliaslookup (#27), fnret/fnparamslookupmod (#28/#31), and
|
||||
// enumlookup (#4a): without the prefer pass a bare-leaf struct
|
||||
// name in module M can collapse onto another module's same-leaf
|
||||
// struct prepended earlier in c.structs, silently picking the
|
||||
// wrong totsize / field offsets.
|
||||
let s: *structinfo = c.structs;
|
||||
for (s != nil) {
|
||||
if (streq(s.sname, name)) {
|
||||
if (streq(s.smod, c.curmod)) { return s; };
|
||||
};
|
||||
s = s.sinext;
|
||||
};
|
||||
s = c.structs;
|
||||
for (s != nil) {
|
||||
let sn: str = s.sname;
|
||||
if (streq(sn, name)) { return s; };
|
||||
s = s.sinext;
|
||||
};
|
||||
// Module-qualified form: `pkg.S` → match the leaf scoped to its
|
||||
// originating module. Mirrors aliaslookup's mod-filter; the
|
||||
// `smod == pkg` guard is what prevents two modules with same-
|
||||
// leaf-name structs from collapsing into whichever entry appears
|
||||
// first in the chain.
|
||||
// Module-qualified form embedded in name (`pkg.S`): scope the
|
||||
// leaf to its originating module. The `smod == pkg` guard
|
||||
// prevents same-leaf structs in two modules from collapsing.
|
||||
let i: i32 = name.len - 1;
|
||||
for (i >= 0) {
|
||||
if (name[i] == 46u8) { // '.'
|
||||
|
||||
Reference in New Issue
Block a user